眾所周知,當某個對象持有着一個Block的時候,如果在Block內部使用強引用反過來持有這個對象,就會導致引用循環。為了避免引用循環,可以使用__weak修飾符,蘋果的官方文檔在用代碼演示__weak修飾符的時候,有這么一個例子:
那么,myController持有着completionHander,在completionHander內部又用一個strongMyController反過來去持有myController,這不也是一個引用循環嗎?為了探究這個問題,可以用下面的方法來測試一下:
1、編寫一個類ViewController,然后在類內編寫方法test,做一個疑似的引用循環:
2、然后通過一個clang命令將這個類轉換成C語言代碼:
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations ViewController.m
3、由此可以得到一個cpp文件,將文件中主要的部分提取出來如下:
4、可以發現:
(1)、在Block結構體中看到,被Block捕獲的變量是
ViewController *const __weak weakSelf;
所以Block本身對self的引用仍然只是弱引用,並不造成引用循環。
(2)、strongSelf只存在於Block對應的函數__ViewController__test_block_func_0里,它的生命周期只在這個函數執行的過程中,函數執行前它不會存在,函數執行完它立刻就被釋放了。
(3)、所以:
①、如果函數執行前self變為nil了,那么函數不會執行,沒有任何引用循環發生;
②、如果函數執行過程中self變為nil了,那么函數一開始聲明的strongSelf會暫時持有着self,此時會有一個暫時的引用循環。當函數執行完(即是Block執行完),strongSelf超出作用域被釋放,引用循環從這里開始打破。接下來,由於沒有任何強引用持有self了,於是self被釋放,最后Block也因為沒有任何強引用持有它也被釋放了。所有對象就都被順利釋放了。
所以最終可以確定:蘋果的演示代碼有可能會造成引用循環,但是只是一個暫時的、可以被打破的引用循環,不會導致內存泄漏。