Swift ざっくり文法 (4)

  • Class
import Foundation

class Myclass {
    let msg:String
    let name:String?
    
    init(msg:String = "hello") {
        self.msg = msg
        self.name = nil
    }
    
    init(msg:String = "hello", name:String) {
        self.msg = msg
        self.name = name
    }
    
    func hello() {
        var helloMsg:String
        if let user = name {
            helloMsg = user + "san" + msg
        } else {
            helloMsg = msg
        }
        print(helloMsg)
    }
}
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let myObj = Myclass()
        myObj.hello() // hello
        let myObjWithMsg = Myclass(msg: "good night")
        myObjWithMsg.hello() // good night
        let myObjWithName = Myclass(name: "Alex")
        myObjWithName.hello() // Alex、hello
        let myObjWithMsgAndName = Myclass(msg: "good night", name: "Alex")
        myObjWithMsgAndName.hello() // Alex、good night
    }
---
}
  • Convenience initializer
class Myclass {
    let msg:String
    let name:String?

-    init(msg:String = "hello") {
-        self.msg = msg
-        self.name = nil
-    }

-    init(msg:String = "hello", name:String) {
+    init(msg:String, name:String) {
        self.msg = msg
        self.name = name
    }
    
+    convenience init (msg:String = "hello") {
+        self.init(msg: msg, name: "Anonymous")
+    }    
}
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let myObj = Myclass()
-        myObj.hello() // hello
+        myObj.hello() // Anonymous, hello
        let myObjWithMsg = Myclass(msg: "good night")
-        myObjWithMsg.hello() // good night
+        myObjWithMsg.hello() // Anonymous,  good night
-        let myObjWithName = Myclass(name: "Alex")
-        myObjWithName.hello() // Alex、hello
        let myObjWithMsgAndName = Myclass(msg: "good night", name: "Alex")
        myObjWithMsgAndName.hello() // Alex、good night
    }
}

convenience initializerを使うと、複数のinitializerがあるときに、別のinitializerを呼ぶことができる。

  • stored property と、computed property
    • stored propertyは、定数・変数で定義される
    • computed propertyは、関数を介して値をやり取りする。値を保持しておらず、設定値を内部的に宣言しておいた定数や変数に設定して保管する
class Circle {
    var radius:Double = 1.0 // stored property
    var area:Double { // computed property
        get{ // property への参照
            return radius * radius * Double.pi
        }
        set(meseki){ // property への値の設定
            radius = sqrt(meseki / Double.pi)
        }
    }
}

let myCicle = Circle()
print("\(myCicle.radius)") // 1.0
print("\(myCicle.area)") // 3.14159265358979
myCicle.area *= 2
print("\(myCicle.radius)") // 1.4142135623731
print("\(myCicle.area)") // 6.28318530717959
myCicle.radius = 3.0
print("\(myCicle.radius)") // 3.0
print("\(myCicle.area)") // 28.2743338823081

read only の property

class Circle {
    var radius:Double = 1.0
    var area:Double {
        get{
            return radius * radius * Double.pi
        }
-        set(meseki){
-            radius = sqrt(meseki / Double.pi)
-        }
    }
}

let myCicle = Circle()
myCicle.radius = 3.0
print("\(myCicle.radius)") // 3.0
print("\(myCicle.area)") // 28.2743338823081

+ myCicle.area = 300 // error: cannot assign to property: 'area' is a get-only property

Read onlyであれば以下のようにも書ける

class Circle {
    var radius:Double = 1.0
    var area:Double {
-        get{
            return radius * radius * Double.pi
-        }
    }
}
  • property observer ( propertyに値がsetされたことをwillSet, didSetで知ることができる )
    • willSetは値が更新される直前に呼ばれ、新しくsetされる値に newValue でアクセスできる
    • didSetは値が更新された直後に呼ばれ、更新前の値に oldValue でアクセスできる
class Player {
    var times = 0
    var level:Int {
        willSet {
            print("------")
            print("willSet \(newValue)")
        }
        
        didSet {
            if oldValue != level {
                times += 1
                print("\(times) update\n\(oldValue) -> \(level)")
            } else {
                print("no changes")
            }
        }
    }
    
    init () {
        level = 0
    }
}

var thePlayer = Player()
thePlayer.level = 10
thePlayer.level = 10
thePlayer.level = 20

// ------
// willSet 10
// 1 update
// 0 -> 10
// ------
// willSet 10
// no changes
// ------
// willSet 20
// 2 update
// 10 -> 20
  • Class property
class Car {
    static var count = 0
    var moving = false
    
    func start() {
        Car.count += 1
        moving = true
    }
    
    func stop() {
        if Car.count > 0 {
            Car.count -= 1
            moving = false
        }
    }
}

let car1 = Car()
let car2 = Car()
print("\(Car.count)") // 0
car1.start()
car2.start()
print("\(Car.count)") // 2
car2.stop()
print("\(Car.count)") // 1
  • Computed class propety
class MyClass {
    static var radian:Double = 0.0
    class var degree:Double {
        get {
            return radian * 90/Double.pi
        }
        
        set(dosu){
            radian = dosu * Double.pi/90
        }
    }
}

MyClass.degree = 18
let katamuki = MyClass.degree
print("\(katamuki)") // 18.0

MyClass.radian = Double.pi/2
let katamuki2 = MyClass.degree
print("\(katamuki2)") // 45.0
  • Class method
class Message {
    class func hello() -> String {
        return "hello"
    }
}

let msg = Message.hello()
print(msg)
  • Access Right
    • access rightを省略するとinternalになる。
    • class がfileprivateだと、property/methodをinternalにすることはできない。
    • fileprivate(set) だとfile外からは、read onlyになる
access right ---
open ---
public Inheritanceとoverrideができない。
internal module外からアクセスできない
fileprivate ファイル外からアクセスできない
private class外からアクセスできない
  • Extension
class Player {
    var name:String = ""
    func hello() {
        print("やあ!" + name)
    }
}

extension Player {
    var who:String {
        get {
            return name
        }
        set(value) {
            name = value
        }
    }
    
    func bye() {
        print("good bye " + name)
    }
}

let obj = Player()
obj.who = "sam" // やあ!sam
obj.hello()
obj.bye() // good bye sam
  • Inheritance
    • A classを継承したB classを作成する。
      • A class は、B classのsuperclass
      • B class は、A classのsubclass
      • B class -> A class
      • 1つのclassが直接継承できるclassは一個だけ
---
class ViewController: UIViewController {
---
}
  • Override
    • superclassと同名のメソッドをsubclass側でも定義してメソッドを上書きする
    • superは、superclassのインスタンス, selfは現在のインスタンス自身
override func viewDidLoad() { // superclass のviewDidLoad()を上書き
    super.viewDidLoad() // superclassのメソッドを実行
    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
  • final ( 継承を禁止したいclass, override されては困るメソッドに付ける。 )
final class MyClass {
    func hello() {
        print("hello")
    }
}
class MyClass {
    final func hello() {
        print("hello")
    }
}
  • Protocol ( classが必ず実装しなければならないpropertyやmethodを指定した仕様書のようなもの。実装しないとエラーになる。 )
    • 読み書き可能なpropertyには、{get set} をつける
    • protocolは他のprotocolを継承できる
protocol GameProtocol {
    var gamePoint:Int {get}
    func hit()
    func miss()
}

- class MyGame:GameProtocol {
+ class MyGame:GameProtocol { // protocol requires function 'miss()' with type '() -> ()'; do you want to add a stub?
    private var total = 0
    var gamePoint: Int {
        return total
    }
    
    func hit() {
        total += 10
        print("hit! +10")
    }

-    func miss() {
-        total /= 2
-        print("you miss, half")
-    }    
+ //    func miss() {
+ //        total /= 2
+ //        print("you miss, half")
+ //    }
}

let myGameObj = MyGame()
myGameObj.hit() // hit! +10
print(myGameObj.gamePoint) // 10
myGameObj.miss() // you miss, half
print(myGameObj.gamePoint) // 5
myGameObj.hit() // hit! +10
print(myGameObj.gamePoint) // 15
  • Enumeration ( 複数の値を一つの型として宣言するために使用。Swiftの列挙型は値だけではなく、propertyやメソッドを持つことができる。)
enum MensSize {
    case S
    case M
    case L
    case XL
}

enum WomenSize {
    case XS, S, M, L
}

var mySize = MensSize.M
mySize = .S // 型推論で、mySizeは、MensSize型になる

var herSize:WomensSize
herSize = .XS

print(mySize) // S
print(herSize) // XS
enum Direction:Int {
    case forward = 1
    case backword // 2
    case right // 3
    case left  // 4
}

let muki1 = Direction.forward
let muki2 = Direction.backword
let muki3 = Direction.right
let muki4 = Direction.left

print(muki1.rawValue) // 1
print(muki2.rawValue) // 2
print(muki3.rawValue) // 3
print(muki4.rawValue) // 4
let muki5 = Direction(rawValue: 3)
if let muki = muki5 { // muki5はnilかもしれないので、Optional Bindingになっている。
    print(muki)
}
  • Enum で型を列挙する
enum Pattern {
    case Monotone(_:PColor)
    case Border(color1:PColor, color2:PColor)
    case Dots(base:PColor, dot1:PColor, dot2:PColor)

    enum PColor:String {
        case red = "red"
        case green = "緑"
        case yellow = "yellow"
        case white = "白"
    }
}

let shirt1 = Pattern.Monotone(.red)
let shirt2 = Pattern.Border(color1: .white, color2: .red)
let shirt3 = Pattern.Dots(base: .yellow, dot1: .white, dot2: .green)
  • Propertyを持ったEnum
enum Ticket {
    case Gold, A, B
    static var name = "入場券"
    
    var area:String {
        get {
            switch self {
            case .Gold:
                return "Gold sheet"
            case .A:
                return "A sheet"
            case .B:
                return "B sheet"
            }
        }
    }
    
    var price:Int {
        get {
            switch self {
            case .Gold:
                return 24000
            case .A:
                return 5000
            case .B:
                return 2000
            }
        }
    }
}

Ticket.name = "hoge live"
print(Ticket.name) // hoge live
let ticket = Ticket.A
print(ticket) // A
print(ticket.price) // 5000
print(ticket.area) // A sheet
  • Methodを持ったEnum
enum Signal:String {
    case Green = "green"
    case Red = "red"
    
    var color:String {
        return self.rawValue
    }
    
    static func description() -> String {
        return "red or green signal"
    }
    
    func isRun() -> Bool {
        if self == .Green {
            return true
        } else {
            return false
        }
    }
    
    mutating func turn() { // 自身を変更するメソッドには `mutating` をつける必要がある
        if self == .Green {
            self = .Red
        } else {
            self = .Green
        }
    }
}

let text = Signal.description()
print(text)  // red or green signal
var lamp = Signal.Green
print(lamp.color) // green
print(lamp.isRun()) // true
lamp.turn()
print(lamp.color) // red
print(lamp.isRun()) // false
  • Struct ( classと違って継承はできない )
struct Box {
    let width:Int
    let height:Int
    let size:String
    let tanka:Int
    var kosu:Int
    
    var price:Int {
        return tanka*kosu
    }
    
    init(width:Int, height:Int, tanka:Int, kosu:Int){
        self.width = width
        self.height = height
        self.tanka = tanka
        self.kosu = kosu
        
        if (width+height) < 250 {
            size = "M"
        } else {
            size = "L"
        }
    }
    
    func sellprice(nebiki:Int = 0) -> Int {
        return price - nebiki*kosu
    }
    
    mutating func discount(count: Int) {
        kosu -= count
    }
}

var box = Box(width: 120, height: 80, tanka: 700, kosu: 6)
print(box) // Box(width: 120, height: 80, size: "M", tanka: 700, kosu: 6)
let selling_price = box.sellprice(nebiki: 10)
print(selling_price) // 4140
box.discount(count: 5)
print(box) // Box(width: 120, height: 80, size: "M", tanka: 700, kosu: 1)
  • Structのcopy
    • Structはclassのobjectと違って、参照型ではなく、値型
    • つまりvariable aに入っているStructをvariable bに代入すると参照ではなく、値が複製されて新しいStructとして代入される
    • String, Array, DictionaryはStruct、だから参照型ではなく、値型
class BoxClass {
    var color:String = "red"
}

struct BoxStruct {
    var color:String = "red"
}

// class
let cBox1 = BoxClass()
let cBox2 = cBox1
cBox2.color = "green"

// struct
let sBox1 = BoxStruct()
var sBox2 = sBox1
sBox2.color = "green"

print("cbox1: \(cBox1.color)") // green
print("cbox2: \(cBox2.color)") // green
print("sbox1: \(sBox1.color)") // red
print("sbox2: \(sBox2.color)") // green
  • Structのsubscript

  • Struct, subscript

struct Stock {
    var name:String
    var data:[String:Int] = [:]
    init(name:String){
        self.name = name
    }
    
    subscript(color:String, size:Double) -> Int {
        get {
            let key = color + String(size)
            if let value = data[key] {
                return value
            } else {
                return 0
            }
        }
        
        set {
            let key = color + String(size)
            data[key] = newValue
        }
    }
}

var shoes = Stock(name: "Tiger")
shoes["green", 24.5] = 3
shoes["green", 25.0] = 5
shoes["red", 26.0] = 5

print(shoes.name) // Tiger
print(shoes["green", 24.5]) // 3
print(shoes["green", 25.0]) // 5
print(shoes["red", 26.0]) // 5

shoes["green", 24.5] -= 2
print(shoes["green", 24.5]) // 1

print(shoes["black", 24.5]) // 0
  • Structのprotocol
protocol Monster {
    var monsterName:String {get}
    var hp:Int {get set}
    mutating func updateHP(pt:Int)
}

struct Bokemon: Monster {
    private(set) var monsterName: String // propertyがread onlyなので、private(set)を付ける
    var hp: Int
    mutating func updateHP(pt: Int) {
        hp += pt
    }
}

var monster = Bokemon(monsterName: "swifty", hp: 200)
print(monster.monsterName) // swifty
print("HP \(monster.hp)") // 200
monster.updateHP(pt: 30)
print("HP \(monster.hp)") // 230

reference from

https://www.amazon.co.jp/dp/4800711843