Block的陷阱總結

1、引發內存泄露
假設一個類有兩個屬性:block和name;

//
//  PHBlockTest.h
//  BlockTestTest
//
//  Created by 項普華 on 2017/2/9.
//  郵箱: [email protected]
//  電話: +86 13316987488
//  主頁: https://github.com/xphaijj
//  Copyright © 2017年 項普華. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface PHBlockTest : NSObject

@property (nonatomic, copy) void(^block)();
@property (nonatomic, copy) NSString *name;

@end
//
//  PHBlockTest.m
//  BlockTestTest
//
//  Created by 項普華 on 2017/2/9.
//  郵箱: [email protected]
//  電話: +86 13316987488
//  主頁: https://github.com/xphaijj
//  Copyright © 2017年 項普華. All rights reserved.
//

@implementation PHBlockTest
- (id)init
{
    if (self = [super init]) {
        self.block = ^{
            NSLog(@"%@",_name);
            NSLog(@"%@",self->_name);
            NSLog(@"%@",self.name);
        };
    }
    return self;
}
@end

普通情況下,block是存在於棧中的,意味着其生命週期由系統管理,不需要我們手動管理。這就存在一個問題,就是我們如果使用block保存一段代碼塊,在回調的時候,說不定已經被系統回收了。
因此我們需要想辦法將block存儲到堆中,方法就是使用copy,做一次複製。(如果使用retain的話,只會將其計數器加一,多做一次強引用,不會重新分類新的內存,所以依然存在於棧中)。 如上代碼塊中的m文件中的

NSLog(@"%@",_name);
NSLog(@"%@",self->_name);
NSLog(@"%@",self.name);

,此時如果在block中調用持有它的對象,就會產生循環引用,造成內存泄露。這個問題怎麼引起的呢? 因爲block存在於堆中,在其代碼塊中引用的對象都會產生一個強指針,而這個時候block本身就是被其引用的對象(copy)強指針指着。這樣就造成了雙方都無法釋放,從而造成內存泄露。解決方案如下:

//
//  PHBlockTest.m
//  BlockTestTest
//
//  Created by 項普華 on 2017/2/9.
//  郵箱: [email protected]
//  電話: +86 13316987488
//  主頁: https://github.com/xphaijj
//  Copyright © 2017年 項普華. All rights reserved.
//

@implementation PHBlockTest
- (id)init
{
    if (self = [super init]) {
        __unsafe_unretained PHBlockTest *temp = self;
        //__weak PHBlockTest *temp = self;
        self.block = ^{
            NSLog(@"%@", temp->_name);
            NSLog(@"%@", temp.name);
        };
    }
    return self;
}
@end

很多時候我使用的是下面的代碼:

 __weak PHBlockTest *temp = self;

和上面的

__unsafe_unretained PHBlockTest *temp = self;

 有什麼區別呢?

 簡單說就是,unsafe,爲什麼不安全呢?因爲當其所修飾的對象釋放時,它並不知道,所以,其地址依然存在,不會清爲nil,這樣第七行代碼

temp->_name
是可以訪問的(在使用時也應當注意這個細節),而使用weak修飾的話,當其修飾的對象被釋放時,temp會被清爲nil,這樣就會出現nil->_name的情況.這是不允許的.
如果想要了解
__unsafe_unretained和__weak的具體區別的話,可查閱其它資料.

爲了防止上面所說的weak釋放問題還有下面一種寫法:

//
//  PHBlockTest.m
//  BlockTestTest
//
//  Created by 項普華 on 2017/2/9.
//  郵箱: [email protected]
//  電話: +86 13316987488
//  主頁: https://github.com/xphaijj
//  Copyright © 2017年 項普華. All rights reserved.
//

@implementation PHBlockTest
- (id)init
{
    if (self = [super init]) {
        __weak PHBlockTest *temp = self;
        self.block = ^{
                    __strong PHBlockTest *strongSelf = temp;//防止在下面使用temp的時候  temp被釋放掉
                    NSLog(@"%@", strongSelf->_name);
                    NSLog(@"%@", strongSelf.name);
        };
    }
    return self;
}
@end
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章