在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提供局部生存期的保證。