相信很多人在一开始学swift的时候都像我一样对?,!,weak,unowned感到十分迷惑。
在这里大概讲述下这些东西在实际开发中的用处。
说真的,这些东西看中文翻译很多时候真的是然并卵
,最好看英文的官方文档,然后不懂的查单词,慢点没所谓的,实在看不懂,多打几次代码就会熟悉很多了,这个是个人体会。
这里是官方文档
###swift变量的类型
swift变量可以理解为有3种:
普通变量
,可选变量(optional)
,隐式解析可选变量(implicitly unwrapped optional)
- 为了方便叙述,optional变量称为
?变量
,隐式解析可选变量称为!变量
####普通变量
- 普通变量不能为nil,如果它是类,结构体中的属性,必须在init()函数中初始化。
####optional变量(?变量) 与 implicitly unwrapped optional 变量(!变量)
- optional是这样定义的:
1 | enum Optional<T> : Reflectable, NilLiteralConvertible { |
关注点在这里:case,和some。
optional变量(
?变量
)为nil的时候,返回的是None
optional变量(
?变量
)不为nil的时候,返回的是Sonm(Type)
,即Type这个类型的普通变量
?变量
使用的时候,可以接受nil值,也可以接受原本类型的值.
- implicitly unwrapped optional是这样定义的:
1 | enum ImplicitlyUnwrappedOptional<T> : Reflectable, NilLiteralConvertible { |
与optional类似,不过使用的时候它是自动在变量后面添加上一个!,默认对这个变进行unwrap,这种操作称为force unwrap。
implicitly unwrapped optional变量(
!变量
)为nil,返回None,并且编译器会报错。
implicitly unwrapped optional变量(
!变量
)不为nil,返回Some(Type),即Type类型的普通变量
这种变量使用起来比较方便,不用在变量后面添加?就可以对变量和函数进行调用,但是比较危险,一旦接受了nil值就会报错导致程序的崩溃。
- 2个例子
最普通的Model定义:
1 | class Model{ |
实在开发的时候,在ViewController中写变量,多把变量定义为!或者?类型
1 | class XXViewController : UIViewController { |
- 开发中值得注意的一点是optional chaining中optional的传递
1 | var modelO: Model? = Model(a: "", b: "") //定义一个?变量 |
Model类中a属性是一个普通变量,modelO是一个是一个?变量,使用optional chaining时,modelO?.a
会是一个?变量,modelO
的optional性质会传递到他的属性a
中, 而modelO!.a
则是解释成普通变量
weak、unowned与循环引用
- weak对应optional,unowned 对应 implicitly unwrapped optional
- weak,unowned定义的变量都是弱引用类型,用于打破循环引用
实际开发中循环引用一般容易出现在这2种情况:
1.block(swift里面称为闭包:closure)
2.定义protocol的时候没有用weak来定义
- 用 unowned 来打破 block 造成的循环引用例子:
1 | func sendHttpRequestWithResponseJSON(block:( (json:String)->Void)? = nil ){ |
上面例子中,在BViewController
中定义了responseBlock
,即BViewController
实例会用一个强指针指向了responseBlock
,而responseBlock
具体实现的时候引用了self,即内部有一个强指针指向了BViewController
的实例,这样就造成了循环引用
当一个BViewController
实例在navigation中Push进来,它就肯定不会为nil,除非被pop出去了。
所以BViewController
实例的self肯定不为nil,可以使用 unowned 来打破循环引用,上面的Block应该如下实现:
1 | responseBlock = {[unowned self](json) -> Void in |
当然也可以用 weak 来打破循环引用,但是self就成为了?类型了
1 | responseBlock = {[weak self](json) -> Void in |
- 用weak来打破 delegate 中的循环引用例子:
1 | //定义一个Protocol |
和block的例子原理相似,造成循环引用的原因是GGUiViewController
与GGTableView
内部变量中相互持有对方,并且都是强引用。
只要这样就可以打破循环引用:
1 | class GGTableView: UIView{ |
总结
- 在ViewController中多用?和!变量
- 打循环引用的时候:
weak
对应?变量
,unowned
对应!变量