为什么需要Optional?
我们在对某个对象操作时可能会返回错误的结果:比如我们将某个字符串转为Int型,执行下面的指令:
letage = response.toInt()
这response如果是用户输入的,可能会输入不确定的数值Do you konw?那么就会得到错误的结果,在OC中遇到类似情况我们怎么处理呢?我们有如下的值可以表示错误:

❌
但是你必须从不同的接口中去选择相应的错误类型,并且要记住这些错误类型,并且如果你忘记了判断,那么编译器是不会提醒你的。为了解决这个问题,Swift中引入了Optional的概念,将这种可能是nil的值进行打包。它可以表示上述所有错误的类型,同时,如果我们使用Optional,就一定要对它进行拆包,使用!号,如果不拆包会造成编译器错误,这就在编译期杜绝了可能在运行时出的错误,如下所示:

Need Unwrap
也可以使用Optional Binding将判断是否有值和拆包结合在一起使用:if let。
varneighbors = ["Alex","Anna","Madison","Dave"]letindex = findIndexOfString("Anna", neighbors)ifletindexValue = index { println("Hello, \(neighbors[indexValue])")}else{ println("Must've moved away")}
当然我们还可进一步使用Optional Binding--Optional Chain:

Optional Chain Binding
在Optional Chain中,只要其中一个Optional的值是nil,那么整个Optional Chain都将是nil,并且不会再执行接下来的取值,如果不是nil则继续执行。这样让我们的代码更加简洁更加安全。
Swift中的Optional其实是个枚举:
enumOptional {caseNonecaseSome(T)}
Swift中的内存管理
在Swift中也用的是ARC,也容易出现循环引用,这时需要使用weak属性。需要注意的是
weak引用的类型是Optional的。
Binding该Optional Type将会产生一个强引用。
如果仅仅是判断即用if判断,则不会产生强引用。
例如:
iflettenant = apt.tenant { tenant.buzzIn() }
但是有些时候我们同时需要weak,又同时需要非Optional的。那么该怎么办?我们需要unowned属性,它也是weak的。
classPerson{varcard: CreditCard?}classCreditCard{unownedletholder: Person init(holder: Person) { self.holder = holder } }
这说明holder没持指向Person,但是holder离开了Person它就不存在了。unowned很像unsafe unretain
Swift中的初始化
在Swift的初始化中需要谨记:
所有的变量在使用前必须初始化
设定完自己所有的变量之后再调用Super的初始化方法
在下面这个初始化的例子中:

init wrong
这样在init方法中没有初始化完自己的hasTurbo变量就直接调用super方法是会在编译的时候报错的,Swift为什么要这么做呢?因为可能会出现如下的情况:

why init wrong
也就是说在父类的init方法中可能会调用filGasTank()这个方法,而这个方法被子类所覆盖了,所以这时候就可能发生意向不到的bug。
初始化方法的覆盖也可能会产生问题:
比如我们有这样一个Car的类:
classCar { var paintColor: Color func fillGasTank() {...} init(color: Color) { paintColor = color fillGasTank() }}classRaceCar: Car { var hasTurbo: Bool init(color: Color, turbo: Bool) { hasTurbo = turbosuper.init(color: color)} convenience init(color: Color) {self.init(color: color, turbo:true)} convenience init() {self.init(color: Color(gray:0.4))} }classFormulaOne: RaceCar { let minimumWeight =642// inherited from RaceCar/*init(color: Color, turbo: Bool) {
hasTurbo = turbo
super.init(color: color)
}
convenience init(color: Color) {
self.init(color: color, turbo: true)
}
convenience init() {
self.init(color: Color(gray: 0.4))
}
*/}
上面注释的内容是从父类中继承过来的,如果我们在子类中调用convenience init(color: Color)这个方法的时候,想让turbo这个参数的默认值为false,这时候我们就需要覆盖掉父类的convenience init方法了。这时我们需要实现自己的designed initializer
classFormulaOne:RaceCar { let minimumWeight =642init(color: Color) { super.init(color: color, turbo:false)}// not inherited from RaceCar/*init(color: Color, turbo: Bool)
convenience init()
*/}
这样以后被注释的内容就不会再被继承了。就会直接掉用子类的designed init方法了。
懒加载属性
如果我们的某个属性需要很大的性能消耗,那么我们希望在使用的时候再创建该类,那么我们不必像在OC中那样重写其get方法,我们只需要在变量声明的前面加上lazy关键字即可。
lazy var color:UIColor=UIColor.red
这样就可以声明了一个懒加载的属性了。
Closures 基本用法
Swift中Array的sort方法实现了Closure,我们来看下:
varclients = ["Pestov","Buenaventura","Sreeram","Babbage"]clients.sort({(a:String,b:String) -> Boolinreturna < b }) println(clients)// [Babbage, Buenaventura, Pestov, Sreeram]
这样就实现了数组中的元素排序。
但是基于Swift强大的类型推断功能,我们可以将其简化为:
clients.sort({ a, binreturna < b})
因为这个Closure是有返回值的,所以编译器可以再次推断,所以我们可以这样写
clients.sort({ a, bina < b })
编译器还可以推断出其参数值,所以,我们这里可以写成
clients.sort({$0<$1})
因为我们还有尾随闭包,所以我们可以进一步简化
clients.sort{$0<$1}
Functional Programming
我们有很多函数式编程的高阶函数可以供调用:
letresult = words.filter{$0.hasSuffix("gry")}.map{$0.uppercaseString}
这样我们就可以找到所有以gry结尾的单词,并且将其转化为大写字母。如果这时结果是
ANGRY
HUNGRY
我们还可以调用reduce方法将其和成一个字符串
letreducedResult = result.reduce("HULK"){"\($0) \($1)"}
这时结果如下:
HULK ANGRY HUNGRY
函数值
比高可以传递一个函数,例如:
numbers.map{ println($0)} numbers.map(println)// 可以将一个函数传递过去var indexes = NSMutableIndexSet()numbers.map{ indexes.addIndex($0)} numbers.map(indexes.addIndex)// 可以将一个Method传过去
闭包是一个ARC对象
我们可以声明一个Closure属性:
varonTempratureChange: (Int) -> Void = {}func logTemperatureDifferences(initial: Int) {varprev = initial onTemperatureChange = { nextinprintln("Changed \(next - prev)°F")prev = next }
因为function也是closure,那么我们可以这样写:
funclogTemperatureDifferences(initial: Int){ var prev = initial funclog(next: Int) { println("Changed \(next - prev)°F")prev = next } onTemperatureChange =log
闭包的循环引用问题
和OC中的Block一样,Swift中也会出现循环引用的问题,我们来看看怎样解决:
classTemperatureNotifier{varonChange: (Int) -> Void = {}varcurrentTemp =72init() { onChange = { tempincurrentTemp = temp }// error: requires explicit 'self' } }
如果出现上面的循环引用问题,编译器会直接报错的,所以我们可以用上文提到的unowned来解决。我们可以将init()方法用下面的来取代:
init() { unownedletuSelf = self onChange = { tempinuSelf.currentTemp = temp }
但是这样写还会出现一个问题,就是如果别处有一份逻辑一样的代码,某个人不注意拷贝过来了忘记将self改成uSelf,或者这个方法很长,写到下面的是忘记了将self改成uSelf,那么就会出现内存泄漏的问题。为了解决这个问题Swift中提出了下面的优雅做法:
init() {onChange = {[unownedself] tempinself.currentTemp = temp } }
Pattern Matching
switch中可以有范围,字符串和数字,并且Enum中可以关联属性,比如:
// case中含有范围funcdescribe(value: Int){switchvalue {case0...4: println("a few")case5...12: println("a lot")default: println("a ton")} }// case中enumTrainStatus {caseOnTimecaseDelayed(Int)}
使用的时候如下:
switchtrainStatus {case.OnTime:} println("on time")case.Delayed(letminutes) :println("delayed by \(minutes) minutes")
我们可以对这个delay做各种各样的匹配:
switchtrainStatus {case.OnTime: println("on time")case.Delayed(1): println("nearly on time")case.Delayed(2...10): println("almost on time, I swear")case.Delayed(_): println("it'll get here when it's ready")
Pattern Compose
也就是说Pattern可以组合出现,一个Pattern中可以包含其它的Pattern,比如对上文的TrainStatus再做以Pattern Compose:
enumVacationStatus {caseTraveling(TrainStatus)caseRelaxing(daysLeft: Int)}switchvacationStatus{case.Traveling(.OnTime): tweet("Train's on time! Can't wait to get there!")case.Traveling(.Delayed(1...15)): tweet("Train is delayed.")case.Traveling(.Delayed(_)): tweet("OMG when will this train ride end #railfail")default: print("relaxing")
Type Pattern
Pattern不仅仅可以作用于Enum,还可以作用于动态的类型,如:Class
func tuneUp(car: Car) {switchcar {caseletformulaOneasFormulaOne: formulaOne.enterPit()caseletraceCarasRaceCar:ifraceCar.hasTurbo { raceCar.tuneTurbo() } fallthroughdefault: car.checkOil() car.pumpTires()} }
这样在多态中就会变得非常有用了。
Tuple Patterns
Tuple pattern有其极其强大的功能,其可以对tuple的各个数值做以类型匹配。
letcolor = (1.0, 1.0, 1.0, 1.0)switch color {case(0.0, 0.5...1.0,letblue, _): println("Green and \(blue * 100)% blue")caselet(r, g, b, 1.0)wherer == g && g == b: println("Opaque grey \(r * 100)%")
我们甚至可以对其中的各个数值做以相应的模式匹配。
Pattern Matching的应用PList校验
比如我们有下面的方法来校验Plist中的内容是否有效
func stateFromPlist(list: Dictionary) -> State?stateFromPlist(["name":"California","population":38_040_000,"abbr":"CA"])
这时我们要对population的值做以限制,如果是字符串返回nil,如果是超过某个范围的时候返回nil,如果是abbr中字母的个数大于2时候我们也返回nil,利用tuple pattern matching的强大特性,我们可以这样去做:
func stateFromPlist(list: Dictionary) -> State? {switch(list["name"], list["population"], list["abbr"]) {case( .Some(letlistNameasNSString), .Some(letpopasNSNumber), .Some(letabbrasNSString) ) where abbr.length ==2:returnState(name: listName,population: pop,abbr: abbr)default:returnnil } }
这就利用了tuple和限制想结合的方式优雅的解决了这个问题。
网友评论