在iOS中,我们经常需要处理引用相关的问题。当我们创建一个指针,指向我们的真实内存区时,这时内存区的引用计数就+1。这里我们要注意分清引用计数是统计真实内存区的,而不是指针,同时要把引用和真实内存区两个概念分开,不要混为一谈。并且要十分注意引用的生存期,因为这关系到真实内存的引用计数的变化。举个例子,我们创建了一个NSString* b,这里特意打出了*以表明b其实是一个指针,真实的内存区对用户来说是不可见的,如果我有一个NSString *a = b。这时是b的引用计数+1了么?在工作中我们经常这样说,但是这却容易让人误解,其实这里的情况是多了一个指向真实内存区的指针a,也就是真实内存区多了一个a引用,其实和b并没有直接关系,如下图
熟悉指针的话这里应该不会有什么问题,我们知道在C中,指针的值就是指针所指向的内存地址,所以*a = b这个指针赋值操作,将指针b的值赋给指针a,换句话说就是让a指向和b同一片内存区。
前面还说道要注意引用指针的生存期,这里最要注意的是局部变量的生存期。局部变量的生存区是它所处的代码块。为什么要特别注意,因为这关系到后面strong的作用。
我们在解决block循环引用时,在block外部使用__weak生成一个对self的弱引用weakSelf,__weak引用的好处就是只是产生一条引用,真实内存区的引用计数不加1,在block内部去使用weakSelf,block捕获weakSelf,生成block对象时,在block对象内部产生一个指向真实内存区的__weak引用。从而解决了block对真实内存区的强引用问题。
我们有时会在block中做一个反向操作,对我们的weakSelf做一个强引用操作,啊?这不又变强引用了么?弄啥嘞??这里就要注意我前面说的引用的生存期了,同时到底是谁引用谁,谁产生了循环引用,当我们在block方法体里面创建一个strongSelf时,strongSelf是一个局部变量,这个局部变量对真实内存区有一个强引用。首先的一个担忧是,这样是不是又产生了循环引用?哈哈,不是的,我们产生循环引用的原因是self强引用了block,block对象在捕获self时生成了一个对self的强引用,这个问题在前面我们使用weakSelf的时候已经解决了,block现在捕获的是weakSelf生成的是一个__weak引用,所以是不会循环引用的,strongSelf是方法体的一个局部变量,说白了和block对象没什么关系,更不可能参杂在block和self的循环引用里面了。
那我们为什么要strongSelf呢,平时我们不strongSelf的时候不是好好的么?是好好的,因为OC的nil为我们操作了一切,让我们的代码不会发生异常,不过在有些情况下还是令人困扰的。首先,我们的strongSelf是用来稍微延长self的生存期的,举个例子,假如你的block方法体内部需要对user类进行赋值,首先以防万一,你在block方法体里面,首先去检查weakSelf是否还活着,如果不巧你发现weakSelf还活着,于是你对user进行了用户id的赋值操作,可是赋值完成后,self的真实内存区被释放了,因为weak引用不会持有对象,你不能保证对象何时被释放,这时之后的用户名头像等都没有赋值成功,之后你这个user对象就开始各种犯错了。这就是weak的弊端,你在方法体执行时检查目标对象不为空,并不能保证在方法体执行结束前它都不为空。
这就是我们要引入strongSelf的原因!!
strongSelf是个局部变量,它的生存期与方法体同在,也就是说你在方法体开始时,保证了strongSelf不为空,那在方法体结束时,它依旧能提供这样的保证。当方法体结束时,strongSelf局部变量的生存期到期,strongSelf被释放,strongSelf对真实内存区的引用也被释放。
另外要注意,strongSelf只在其生存期起作用,也就是说,当你尚未执行到block的方法体时,strongSelf是不会起作用的,这很明显,因为它还没执行,但是我觉得还是有必要明确的说出来。
在SD中,在下载图片的block回调中就是使用了这种思想来判断self是否已经被销毁的,如果没有销毁就使用strongSelf提供局部生存期的保证。