iOS容易造成迴圈引用的三種場景

2021-09-07 02:19:09 字數 4157 閱讀 3614

(1)計時器nstimer

一方面,nstimer經常會被作為某個類的成員變數,而nstimer初始化時要指定self為target,容易造成迴圈引用。 另一方面,若timer一直處於validate的狀態,則其引用計數將始終大於0。先看一段nstimer使用的例子(arc模式):

1 #import 2 @inte***ce friend : nsobject

3 - (void)cleantimer;

4 @end

1 #import "friend.h"

2 @inte***ce friend ()

3 6 @end

7 8 @implementation friend

9 - (id)init

10 15 return self;

16 }

17 18 - (void)handletimer:(id)sender

19 22 - (void)cleantimer

23 27 - (void)dealloc

28

在類外部初始化乙個friend物件,並延遲5秒後將friend釋放(外部執行在非arc環境下)

1         friend *f = [[friend alloc] init];

2 dispatch_after(dispatch_time(dispatch_time_now, 5*nsec_per_sec), dispatch_get_main_queue(), ^);

我們所期待的結果是,初始化5秒後,f物件被release,f的dealloc方法被呼叫,在dealloc裡面timer失效,物件被析構。但結果卻是如此:

1

2

3

4

5

6

7

8

9

10

2015-03-18 18:00:35.300 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:36.299 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:37.300 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:38.299 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:39.299 wzlcodelibrary[41422:3390529] friend say: hi!//執行了5次後沒按照預想的停下來

2015-03-18 18:00:40.299 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:41.300 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:42.300 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:43.299 wzlcodelibrary[41422:3390529] friend say: hi!

2015-03-18 18:00:44.300 wzlcodelibrary[41422:3390529] friend say: hi!

.......根本停不下來.....

這是為什麼呢?主要是因為從timer的角度,timer認為呼叫方(friend物件)被析構時會進入dealloc,在dealloc可以順便將timer的計時停掉並且釋放記憶體;但是從friend的角度,他認為timer不停止計時不析構,那我永遠沒機會進入dealloc。迴圈引用,互相等待,子子孫孫無窮盡也。問題的癥結在於-(void)cleantimer函式的呼叫時機不對,顯然不能想當然地放在呼叫者的dealloc中。乙個比較好的解決方法是開放這個函式,讓friend的呼叫者顯式地呼叫來清理現場。如下:

1

2

3

4

5

friend *f = [[friend alloc] init];

dispatch_after(dispatch_time(dispatch_time_now, 5*nsec_per_sec), dispatch_get_main_queue(), ^);

(2)block

block在copy時都會對block內部用到的物件進行強引用(arc)或者retaincount增1(非arc)。在arc與非arc環境下對block使用不當都會引起迴圈引用問題,一般表現為,某個類將block作為自己的屬性變數,然後該類在block的方法體裡面又使用了該類本身,簡單說就是self.someblock = ^(type var);block的這種迴圈引用會被編譯器捕捉到並及時提醒。舉例如下,依舊以friend類為例子:

#import "friend.h"

@inte***ce friend ()

@property (nonatomic) nsarray *arr;

@end

@implementation friend

- (id)init

; }

return self;

}

我們看到,在block的實現內部又使用了friend類的arr屬性,xcode給出了warning, 執行程式之後也證明了friend物件無法被析構:

網上大部分帖子都表述為"block裡面引用了self導致迴圈引用",但事實真的是如此嗎?我表示懷疑,其實這種說法是不嚴謹的,不一定要顯式地出現"self"字眼才會引起迴圈引用。我們改一下**,不通過屬性self.arr去訪問arr變數,而是通過例項變數_arr去訪問,如下:

由此我們知道了,即使在你的block**中沒有顯式地出現"self",也會出現迴圈引用!只要你在block裡用到了self所擁有的東西!但對於這種情況,目前我不知道該如何排除掉迴圈引用,因為我們無法通過加__weak宣告或者__block宣告去禁止block對self進行強引用或者強制增加引用計數。對於self.arr的情況,我們要分兩種環境去解決:

1)arc環境下:arc環境下可以通過使用_weak宣告乙個代替self的新變數代替原先的self,我們可以命名為weakself。通過這種方式告訴block,不要在block內部對self進行強制strong引用:(如果要相容ios4.3,則用__unsafe_unretained代替__weak,不過目前基本不需考慮這麼low的版本)

1          self.arr = @[@111, @222, @333];

2 __weak typeof(self) weakself=self;

3 self.block = ^(nsstring *name);

2)mrc環境下:解決方式與上述基本一致,只不過將__weak關鍵字換成__block即可,這樣的意思是告訴block:小子,不要在內部對self進行retain了!

(3)委託delegate

在委託問題上出現迴圈引用問題已經是老生常談了,本文也不再細講,規避該問題的殺手鐗也是簡單到哭,一字訣:宣告delegate時請用assign(mrc)或者weak(arc),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉迴圈引用了!

**

iOS容易造成循引用的場景

timer就是乙個能在從現在開始的未來的某乙個時刻又或者週期性的執行我們指定的方法的物件 nstimer執行的必要條件 對應執行緒的runloop要開啟,mode要對應 下面看timer的迴圈引用 void setblock testblock block id init return self v...

iOS中解決NSTimer迴圈引用的三種方式

今天有個人來公司面試,問了他平時在使用timer定時器時怎麼解決迴圈引用的問題。然後就得到了這樣乙個答案 weak typeof self weakself self self.timer nstimer scheduledtimerwithtimeinterval 1.0 target weaks...

PHP的三種迴圈

while 只要條件為真就迴圈 for 提前知道迴圈的次數 foreach 用來迴圈陣列,指標控制 和while for控制迴圈次數的條件不同 當物件被foreach的時候,內部的valid,current,key方法會依次被呼叫,其返回的值便是foreach語句的key和value。當一次迴圈體結...