Translate

bloggerads內文

2017年4月16日 星期日

IOS中viewController被POP後不進入dealloc的問題

在寫程式碼時候如遇到當離開頁面時,不會進入dealloc的時候,代表有物件還沒被釋放記憶體,這邊列出小編有遇到的問題情況.


ARC下可以重寫dealloc方法並在viewController被釋放後自動調用,重寫該方法時不能顯式調用[super dealloc],因為系統會自動幫你調用父類的dealloc方法。
控制器在被pop後會被釋放,但有些時候會發現控制器有時候不會調用dealloc方法,歸根結底,是因為當前控制器被某個對象強引用了,控制器的引用計數不為0,系統無法幫你釋放這部分記憶體。原因大致有以下幾點:
控制器中NSTimer沒有被銷毀
當viewController中存在NSTimer時,需要特別注意,當調用
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:)  userInfo:nil repeats:YES]
時,因為 target:self ,也就是引用了當前viewController,導致控制器的引用計數加1,如果沒有將這個NSTimer 銷毀,它將一直保留該viewController,無法釋放,也就不會調用dealloc方法。所以,需要在viewWillDisappear之前需要把控制器用到的NSTimer銷毀。
[timer invalidate]; // 銷毀timer
timer = nil; // 設置nil
viewController中的代理不是weak屬性
例如@property (nonatomic, weak) id delegate;代理要使用弱引用,因為自定義控件是加載在視圖控制器中的,視圖控制器view對自定義控件是強引用,如果代理屬性設置為strong,則意味著delegate對視圖控制器也進行了強引用,會造成循環引用。導致控制器無法被釋放,最終導致記憶體洩漏。
viewController中block的循環引用
在ARC下,block會把它裡面的所有對象強引用,包括當前控制器self,因此有可能會出現循環引用的問題。比如viewController中有個block屬性,在block中又強引用了self或者其他成員變量,那麼這個viewController與自己的block屬性就形成循環引用,導致viewController無法釋放。
// ARC enabled
/************** MyObject Class **************/

typedefvoid(^myBlock)(void);
@interfaceMyObject:NSObject
{
    myBlock blk;
}
@end

@implementationMyObject
- (id)init
{
    self=[superinit];
    blk = ^{
            NSLog(@"self = %@",self);
    };
    returnself;
}
- (void)dealloc
{
    NSLog(@"dealloc");
}
@end
/************** main function **************/
int main()
{
    id myObject=[[MyObjectalloc] init];
    NSLog(@"%@",myObject);
    return 0;
}
由於self是__strong修飾,在ARC 下,當編譯器自動將代碼中的block 從棧拷貝到堆時,block 會強引用和持有self,而self恰好也強引用和持有了block,就造成了傳說中的循環引用。

ddddd .png
由於循環引用的存在,造成在main()函數結束時,記憶體仍然無法釋放,即記憶體洩露。編譯器也會給出警告信息
warning: capturing 'self' strongly in this block is likely to lead to a retain cycle [-Warc-retain-cycles]
blk = ^{NSLog(@"self = %@", self);};
note: Block will be retained by an object strongly retained by the captured object
blk = ^{NSLog(@"self = %@", self);};
為了避免這種情況發生,可以在變量聲明時用weak修飾符修飾變量self,讓 block 不強引用self,從而破除循環。 iOS4 和 Snow Leopard 由於對 weak 的支持不夠完全,可以用unsafe_unretained代替。
- (id)init
{
    self = [super init];
    id __weak tmp = self;
    blk = ^{NSLog(@"self = %@", tmp);}; 
    return self;
}

Paste_Image.png
再看一個例子:
@interface MyObject : NSObject
{
   myBlock  blk;
   id _obj; 
}
@end
@implementation MyObject 

- (id)init
{
     self = [super init];
     blk = ^{ NSLog(@"_obj = %@", _obj); }; 
     return self;
}
...
...
@end
上面的例子中,雖然沒有直接使用 self,卻也存在循環引用的問題。因為對於編譯器來說,_obj就相當於self->_obj,所以上面的代碼就會變成
 blk = ^{ NSLog(@"_obj = %@", self->_obj); };
出處來源:http://www.jianshu.com/p/58f43fcbc197

沒有留言:

張貼留言