読者です 読者をやめる 読者になる 読者になる

woshidan's blog

そんなことよりコードにダイブ。

はてな教科書の「プログラミング言語 Swift」を読んでます 4

iOSお遍路 iOS Swift

https://github.com/hatena/Hatena-Textbook/blob/master/swift-programming-language.md

のんびり読んでいきます。

Reference types

参照型の値は、変数間で同じ状態を共有する。

参照型の変数の場合、オブジェクトの参照先をコピーして、オブジェクトの中身自体は共有してる、という感じ。

 271> class DecoString { 
 272.     var string: String 
 273.     init(_ given: String) { 
 274.         self.string = given 
 275.     } 
 276.     func printWithDecoration() { 
 277.         print(string) 
 278.     } 
 279. } 
 280> var decoStr1 = DecoString("first")
decoStr1: DecoString = {
  string = "first"
}
 281> var decoStr2 = decoStr1
decoStr2: DecoString = {
  string = "first"
}
 282> decoStr1.string = "second"
 283> decoStr2.printWithDecoration()
second

Classes

コンストラクタはinitで記述。

 271> class DecoString { 
 272.     var string: String 
 273.     init(_ given: String) { 
 274.         self.string = given 
 275.     } 
 276.     func printWithDecoration() { 
 277.         print(string) 
 278.     } 
 279. } 

Automatic Reference Counting

  • 参照型の値は参照カウント方式のメモリ管理が行われる
  • コンパイル時に参照カウントの増減処理がコンパイラによって挿入される
    • 変数にインスタンスへの参照が格納されるとき参照カウントは1増え、その変数がスコープを外れるとき参照カウントは1減る
  • 参照カウントが1以上ならインスタンスはメモリ上に保持される(メモリリーク)
  • 2つのインスタンスが互いに参照しあう場合は、片方を弱い参照にする
    • 弱い参照の変数は必ず Optional である
  • 弱い参照は weakキーワード、あるいは unownedキーワードを使って設定する
    • weakキーワードの場合、インスタンスが解放された場合は、即座に変数の値が nil
    • unownedキーワードの場合、インスタンスが解放されても nil にならないが、解放後にアクセスすると実行時エラー
  • クロージャは基本的にキャプチャした参照型の値について強い参照を持っている
    • クロージャにおいても、必要に応じて weakunowned の弱い参照にする必要がある
// キャプチャで `weak` の弱い参照にするときのシンタックス
 310> class Dog { 
 311. }
 312> let dog = Dog()
dog: Dog = {}
 313> let callDog = { [weak dog] (message: String) -> String in 
 314.     return "\(dog!), \(message)." 
 315. }
callDog: String -> String = 0x0000000101e13000 $__lldb_expr343`__lldb_expr_342.(closure #1) at repl342.swift
 316> callDog("stay")
$R58: String = "Dog, stay."

Properties and methods

Stored Properties

  • Stored プロパティは let や var で値を保持する。
    • enum型には持たせられない
  • .propertyNameでアクセス可能
  • lazyキーワードで遅延初期化が可能
// 遅延初期化の例
 317> class DataFormatter { 
 318.     var format: String = "" 
 319. } 
 320.  
 321. class DataPrinter { 
 322.     lazy var formatter = DataFormatter() 
 323.     var data: [String] = [] 
 324. }
 325>  
 326> let printer = DataPrinter()
printer: DataPrinter = {
  formatter.storage = nil
  data = 0 values
}
 327> printer.formatter
$R59: DataFormatter = {
  format = ""
}
 328> printer
$R60: DataPrinter = {
  formatter.storage = (format = "") {
    format = ""
  }
  data = 0 values
}

Computed Properties

Computed プロパティでは他の情報から計算可能な値をプロパティとして提供できる。

少し計算して返していたメソッドなどを Computed プロパティとして定義すると良さそうです。

 329> struct Square { 
 330.     var length : Double = 0.0 
 331.     var area: Double { 
 332.         get { 
 333.             return pow(length, 2) 
 334.         } 
 335.         set(newArea) { 
 336.             length = sqrt(newArea) 
 337.         } 
 338.     } 
 339. } 
 340> var square1 = Square()  
square1: Square = {
  length = 0
}
 341> square1.length = 3.0 
 342> square1.area
$R61: Double = 9
 343> square1.area = 10
 344> square1.length
$R62: Double = 3.1622776601683795
 345>  

また、getブロックしか利用しない場合は、get/setブロックを省略することができます。

Property Observers

  • willSetdidSetブロックを用いてプロパティの値の変化の前後に何らかの処理を行うことができます。
  • 特に指定していない場合、各ブロックでnewValue や oldValue で変更の前後の値を得ることができます。
  • またdidSetで値を別の値に変更することも可能です。
 346> struct Dam { 
 347.     let limit = 100.0 
 348.     var waterLevel = 0.0 { 
 349.         willSet { 
 350.             print("\(newValue - waterLevel) will change") 
 351.         } 
 352.         didSet { 
 353.             if waterLevel > limit { 
 354.                 print("Bursted") 
 355.                 waterLevel = limit 
 356.             } 
 357.             print("\(waterLevel - oldValue) did change") 
 358.         } 
 359.     } 
 360. }
 361> var dam = Dam()
dam: Dam = {
  limit = 100
  waterLevel = 0
}
 362> dam.waterLevel = 120 
120.0 will change
Bursted
100.0 did change

Type Properties

typeキーワードを使って、型に対する変数が作れます。いわゆるクラス変数。

Methods

  • 型は関数を持つことができて、これをメソッドという
  • メソッドの実装ではselfキーワードで自身を指す
  • 自身の内部状態を変化させるものには mutatingキーワードを funcの前につけます
  • enumの場合はselfに直接代入することで状態を変化させます
 363> enum Weekly { 
 364.     case Monday 
 365.     case Tuesday 
 366.     case Wednesday 
 367.     case Thursday 
 368.     case Friday 
 369.     case Saturday 
 370.     case Sunday 
 371.      
 372.     mutating func past() { 
 373.         switch self { 
 374.     case .Monday: 
 375.           self = .Tuesday 
 376.         case .Tuesday:
 377.             self = .Wednesday 
 378.         case .Wednesday: 
 379.             self = .Thursday 
 380.         case .Thursday: 
 381.             self = .Friday 
 382.         case .Friday: 
 383.             self = .Saturday 
 384.         case .Saturday: 
 385.             self = .Sunday 
 386.         case .Sunday: 
 387.             self = .Monday 
 388.         } 
 389.     } 
 390. } 
 // enum型はその中のどれか、みたいな形で初期化するのだった...
 391> var day = Weekly()
repl.swift:391:11: error: 'Weekly' cannot be constructed because it has no accessible initializers
var day = Weekly()
          ^

 391> var day = Weekly.Monday
day: Weekly = Monday
 392> day.past()
 393> day
$R63: Weekly = Tuesday
 394> day.past() 
 395> day
$R64: Weekly = Wednesday
 396> day.past() 
 397> day 
$R65: Weekly = Thursday

Type Methods

staticをつけることで、タイプメソッドとなります。Rubyのクラスメソッドのように、selfは型自身を指します。

メソッドは static, プロパティは type.

Subscripts

これ、数値によるUtil的なものか、case分の代わりに使うことがあるようにしか見えないのですが、書き始めると変わるのでしょうか...

 398> struct OddNumbers { 
 399.     subscript(index: Int) -> Int { 
 400.         return index * 2 
 401.     } 
 402. }
 403> let odds = OddNumbers()
odds: OddNumbers = {}
error: could not fetch result -- Couldn't apply expression side effects : Couldn't dematerialize odds: corresponding symbol wasn't found

 404> odds
$R66: OddNumbers = {}
 405> odds[3]
$R67: Int = 6

今日はここまで...。