引言
The Swift Programming Language已经泛泛地看了几遍了,由于看的不上心,花时间研究Swift的时候寥寥无几,再加上平时用Swift写的代码很少,一直还是Swift菜鸟一枚,现在打算和Swift死磕,先从The Swift Programming Language开始。
注:文中引用部分无特殊说明都是The Swift Programming Language里的原文
基础部分
Swift基于cocoa、cocoa touch,兼容OC,能够边写代码边执行(Playground),它是一种语法灵活,编译严格的一门语言。
32位平台上Int与Int32位数相同,64位平台上Int与Int64位数相同
Double是64位的,它至少有16位数字,后面五舍六入,如果是0.xxx形式的小数会显示17位
Float是32位的,只显示7位数字,后面进五舍六入,如果是0.xxx形式的小数会会显示8位
这里与programming Language中说明的不符,我是在playground里测试的
下面是原文表述
注意:
Double精确度很高,至少有15位数字,而Float只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。
推断浮点数的类型时,Swift总是会选择Double,而不是Float,表达式中整型、浮点同时出现会被推断为Double
不同进制的整数数值字面量的表示:
0b二进制数、0o八进制数、0x十六进制数
1.25e2 = 1.25 x 10^2 = 125.0
1.25e-2 = 1.25 x 10^-2 = 0.0125
0xFp2 = 15 x 2 ^ 2 = 60
0xFp-2 = 15 x 2^ -2 = 3.75
数值字面量可以添加格外格式增加可读性:
let num1 = 000123.456
let num2 = 1_000_000
let num3 = 1_000_000.000_000_1
---
整数转换要向精度大的一方转换
>```
let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
这里注意:UInt16(one)不同于OC中的强转,而是调用了UInt16类的构造函数,该类的构造函数可以接受一个UInt8类型的对象罢了
整型和浮点进行运算,统一先转成浮点,字面量本身没有类型之分,3和1.5是可以直接相加的,但是:
let num1 = 3
let num2 = 1.5
num1和num2就不能直接相加,num1和num2是有类型之分的。
给类型取别名
typealias AudioSample = UInt16
Bool 类型:
值是true或false,在需要Bool值的时候如果传入非Bool值,swfit会报错,例如if判断的时候必须是Bool值,这个与OC不同。
元组(tuples)形式和用法都很简单,基本看一下就知道怎么用,元组作为函数的返回值很有用
let http404Error = (404, "Not Found")
let (statusCode, statusMessage) = http404Error
print("The status code is (statusCode)")
let (justTheStatusCode, _) = http404Error
print("The status code is (http404Error.0)")
let http200Status = (statusCode: 200, description: "OK")
print("The status code is (http200Status.statusCode)")
---
###可选:Optional
Swift不允许你声明一个变量,而不对其初始化,这一点OC就很宽泛,但是有时候严格的编译会产生一些问题:
例如,我们都知道VC有个view属性,但是也都知道view属性的赋值并不是在VC初始化的时候进行的,而是在loadView函数中进行的,这在Swift中就是个问题,实际上lazy load或者在声明之后再初始化的场景还是很多的。
Swift如何解决这个问题?答案:可选(Optional,用在类型后面加?表示)
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
//系统源码,忽略下面的代码
}
Optional可选有两个值:None或Some(Wrapped)。
OC中的nil表示缺少一个合法对象,仅用于修饰oc对象,非oc对象用NSNotFound表示,这是Swift与OC很不同的地方,这也体现了swift是编译严格的语言。
let a : Int = 1 //a是一个Int对象(swift这里的设计参照了java一切皆对象的思想)
let b : Int? = 1 //b是一个Optional对象,对象值是Some(Wrapped)因为此时b是赋值了的,而这里的Wrapped指的就是Int
let c : Int? = nil //c是一个Optional对象,对象值是None
![](http://upload-images.jianshu.io/upload_images/1490498-cf6c43f22d4274bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
c是Optional类型的对象,其实是一个enum,是可以用nil初始化的,其实就是enum中的None,d是一个Int对象是不能用nil初始化的。
###强制拆包!:
通过上面的了解我们知道:一个用?修饰的对象再也不是你想象中的那个对象了,**所有用?修饰的类型所产生的对象都是Optional对象**,这对理解?和!很重要的。
例如:上面例子中的b是一个Int?类型,编译器把他当成是一个Optional对象来处理,而不是Int对象来处理。
![注意看右边的输出](http://upload-images.jianshu.io/upload_images/1490498-fad950063a256c9e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
问题来了:
我用b是要把它当一个Int对象来用,它现在是一个Optional对象怎么用?
答案是:用"!"进行强制拆包(forced unwrapping).就可以得到里面的值,要么是None,要么是Some(Wrapped),在这里,Some(Wrapped)就是Int。
![注意看右边的输出和上面的图对比来理解?和!](http://upload-images.jianshu.io/upload_images/1490498-ba7e3e8576a31ed9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
问题又来了:
!有什么用?
答案,!十分有用,一旦你用?声明声明了一个变量,它就不再是你期望的那个类型了,它是Optional类型了(反复强调了N遍,别嫌烦啊),所以它就**不能和其他类型愉快的玩耍了**。
![](http://upload-images.jianshu.io/upload_images/1490498-dd08610731e947ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
a和b仅仅是在声明的时候差了一个?就不能在一起玩耍了,这个时候就要用!强制拆包来解决问题了,我们对可选类型进行拆包后的对象就是我们期望的对象,就可以和其它相同对象一起玩耍了。
![](http://upload-images.jianshu.io/upload_images/1490498-f3dc950e7af978de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
###可选绑定(optional binding):
>使用*可选绑定(optional binding)*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在if和while语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。
if let constantName = someOptional {
statements
}
我们可以通过一个很简单的例子把可选绑定理解的更透彻一点:
![](http://upload-images.jianshu.io/upload_images/1490498-57272568fe5adfa4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通过例子和programming Language中的说明我们可以做如下推断:
1.我们用 if let tempX = x这种表达的时候,x一定是一个可选类型,否则报错(tempA)
2.if let tempX = x 这种表达形式可以理解成:**判断可选x是否包含值,如果包含值,赋值成功,如果不包含值,赋值失败,赋值成功、失败作为if判断的Bool条件,也就是说if判断的是let tempX = x这个整体,与tempX赋值后是什么类型无关**,这里需要理解一下,还有一点是tempX = x赋值成功后其实并不是单纯的赋值,**x是先拆包后赋值给tempX的**,这对于理解可选绑定很重要(tempB,及其输出信息)。
3.可选绑定的变量或常量只能在if中使用,else中是不能用的(tempC)。
有了上面例子对可选绑定的理解,再理解programming Language中出现的这段代码就比较简单了:
>```
if let actualNumber = Int(possibleNumber) {
print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
print("\'\(possibleNumber)\' could not be converted to an integer")
}
Int(possibleNumber)这种情况上面已经介绍过,就是调用Int类的构造函数,这个构造函数返回的是一个Int?对象,因为用possibleNumber并不一定能保证初始化Int成功,如果possibleNumber="haha"初始化就会失败,所以返回的是Int?而不是Int,理解了这一点,再加上上面可选绑定的例子,这个代码就一清二楚了。
你可以包含多个可选绑定在if语句中,并使用where子句做布尔值判断。
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
// prints "4 < 42"
隐式解析可选类型:
let a :Int? = 1
我们之前介绍的可选有一个问题,我们实际需要的是Int,但是Int?给我的是一个可选Optional对象,里面包了一个Some(Wrapped)是Int,这使得我们每次用a的时候都要对Optional对象拆包才能用,很是麻烦。
如何解决这个问题?
答案:隐式解析可选类型
隐式解析可选类型:在声明常量或变量的时候用"!"代替"?"就是声明了一个隐式解析可选类型,有点晕?看个例子就清楚了。
我们在用普通可选的时候(就是用?修饰的),每次使用都要拆包,才能用,而隐式解析可选类型不需要,直接就可以用,是不是方便了很多,怎么理解隐式解析可选类型?
有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。
这是隐式解析可选类型存在的意义。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
这是隐式解析可选类型的作用
我们可以理解隐式解析可选类型是对普通可选进行自动拆包,省去了我们手动拆包的麻烦,但是如果一个隐式解析可选类型如果没有值的时候,你去尝试取它的值就会发生运行时错误,就像一个普通可选类型强制拆包(!)但是它却没有值的时候也会发生运行时错误。普通可选和隐式解析可选侧重点不同,前者是安全,后者是方便,如何取舍由自己决定。
我们同样可以在if和while里让隐式解析可选类型和nil进行比较(只有可选类型才能和nil进行比较,因为普通类型的值是不可能是nil的),也可以对其进行可选绑定。
还记得我们引入可选Optional的时候引入的VC的view属性的例子吗,它就是一个隐式解析可选类型
//系统源码
publicvar view: UIView! // The getter first invokes [self loadView] if the view hasn't been set yet. Subclasses must call super if they override the setter or getter.
个人理解:可选、隐式解析可选是swift严编译的一个很好的提现,就这一点来说swift对于提高我们编程的严密性还是有一定帮助的。
Swift中断言和OC差不多
assert(age > 0, "A person's age cannot be less than zero")
断言信息可省略
assert(age > 0)
release环境或者基于release复制出来的新的编译环境下,断言是失效的
总结
作为Swift菜鸟,在死磕Swift的道路上希望多与大家沟通讨论,多向大家学习。