Intermediate Swift

Intermediate Swift - Session 403

Options

String to Integer
Optional Type
var optionalNumber:Int?
// default initialized to nil
optionalNumber = 6

Non-Optional Types can't be nil
var myString:String = nil
// error

Optional Return Types

func findIndexOfString(string:String, array:String[]) -> Int? {
    for (index, value) in enumerate(array) {
        if value == string {
            return index
        }
    }
    return nil
}

Optional Binding

Test and unwrap at the same time

var neighbors = ["Alex", "Anna", "Madison", "Dave"]
let index = findIndexOfString("Anna", neighbors)
//if let indexValue = index { // index is of type Int, not nil?, indexValue is of type Int
if let indexValue = findIndexOfString("Anna", neighbors) { // index is of type Int, not nil?, indexValue is of type Int
 println("Hello, \(neighbors[indexValue])")
}
else {
    println("Must've moved away")
}

Optional Chaining

if let addressNumber = paul.residence?.address?.buildingNumber?.toInt() {
addToDatabase("Paul", addressNumber)
}

  • Use if let optional binding to test and unwrap at the same time
  • Option chaining (?) is a concise way to work with chained optionals

Memory Management

Automatic Reference Counting
Weak References are optional values
Binding the optional produces a strong reference

if let tenant = apt.tenant {
    tenant.buzzIn()
}
apt.tenant?.buzzIn()

Unowned References

Strong, Weak, and Unowned References
strong references are default

Initialization

Every value must be initialized before it is used.

class RaceCar:Car {
    var hasTurbo:Bool
    init(color:Color, turbo:Bool) {
        hasTurbo = turbo
        super.init(color:color)
    }
    // 多個 initializers
 convenience init(color:Color) {
        self.init(color:color, turbo:true)
    }

    convenience init() {
        self.init(color:Color(gray:0.4))
    }
}
class FormulaOne:RaceCar {
    let minimumWeight = 642

    init(Color:Color) {
        self.init(color:color, turbo:false)
    }
}

Lazy Properties

@lazy var multiplayerManager = MultiplayerManager()

Deinitialization

class FileHandle {
    let fileDescriptor:FileDescriptor
    init(path:String) {
        fileDescriptior = openFile(path)
    }
    deinit {
        closeFile(fileDescriptor)
    }
}

Initialize all values before you use them
set all stored properties first, then call super.init

Designated initializers only delegate up
Convenience initializers only delegate across

Deinitializers are there... if youe need them

Closures

var clients = ["Pestov", "Buenaventura", "Sreeram", "Babbage"]
clients.sort({(a:String, b:String) -> Bool in
  return a < b
})
println(clients)

Type Inference 簡化成

struct Array<String> {
  func sort(order:(String, String) -> Bool)
}

clients.sort({a, b in a < b})
Implicit Arguments

clients.sort({ $0 < $1 })

Trailing Closures

client.sort { $0 < $1 }

Functional Programming
println(words.filter { $0.hasSuffix("gry") })
// angry
// hungry
println(words.filter { $0.hasSuffix("gry") }
  .map { $0.uppercaseString })
// ANGRY
// HUNGRY
Function Values - Closures
numbers.map {
//  sum += $0
  println($0)
}
// 可以簡化成
numbers.map(println)

var indexes = NSMutableIndexSet()
numbers.map {
  indexes.addIndex($0)
}
// 可以簡化成
numbers.map(indexes.addIndex)
Capture Lists
class TempNotifier {
    var onChange:(Int) -> Void = {}
    var currentTemp = 72

    init() {
        onChange = {[unowned self] temp in
            self.currentTemp = temp
        }
    }
}
Pattern Matching
Validating a Property List
func stateFromPlist(list:Dictionary<String, AnyObject>) -> State?
// 正常狀況
stateFromPlist("name": "California"
                             "population", 38_040_000,
                             "abbr": "CA")
// 當傳入錯誤的型別
// ie: population 應該要傳入數字,卻傳入字串,需要回傳 nil
stateFromPlist("name": "California"
                            "population", "hella peeps",
                            "abbr": "CA")

可以用這樣的方式檢查

func stateFromPlist(list:Dictionary<String, AnyObject>) -> State? {
    var name:NSString?
    switch list["name"] {
        case .Some(let listName as NSString):
            name = listName
        // ...
     default:
            name = nil
    }
    return name
}

有更好的方法

func stateFromPlist(list:Dictionary<String, AnyObject>) -> State? {
    switch (list["name"], list["population"], list["abbr"]) {
        case (.Some(let listName as NSString),
                    .Some(let pop as NSNumber),
                    .Some(let abbr as NSString))
        where abbr.length ==2:
            // 用 where 檢查 abbr 長度是否為2
         return State(name:listName, population:pop, abbr:abbr)
        default:
            return nil
    }
}

總結

  • Optionals
  • Memory management
  • Initialization
  • Closures
  • Pattern matching