1. Block的聲明和線程安全
Block屬性的聲明,首先需要用copy
修飾符,因為只有copy
后的Block才會在堆中,棧中的Block的生命周期是和棧綁定的,可以參考之前的文章(iOS: 非ARC下返回Block)。
另一個需要注意的問題是關於線程安全,在聲明Block屬性時需要確認“在調用Block時另一個線程有沒有可能去修改Block?”這個問題,如果確定不會有這種情況發生的話,那么Block屬性聲明可以用nonatomic
。如果不肯定的話(通常情況是這樣的),那么你首先需要聲明Block屬性為atomic
,也就是先保證變量的原子性(Objective-C並沒有強制規定指針讀寫的原子性,C#有)。
比如這樣一個Block類型:
typedef void (^MyBlockType)(int);
屬性聲明:
@property (copy) MyBlockType myBlock;
這里ARC和非ARC聲明都是一樣的,當然注意在非ARC下要release
Block。
但是,有了atomic
來保證基本的原子性還是沒有達到線程安全的,接着在調用時需要把Block先賦值給本地變量,以防止Block突然改變。因為如果不這樣的話,即便是先判斷了Block屬性不為空,在調用之前,一旦另一個線程把Block屬性設空了,程序就會crash,如下代碼:
if (self.myBlock)
{
//此時,走到這里,self.myBlock可能被另一個線程改為空,造成crash
//注意:atomic只會確保myBlock的原子性,這種操作本身還是非線程安全的
self.myBlock(123);
}
所以正確的代碼是(ARC):
MyBlockType block = self.myBlock;
//block現在是本地不可變的
if (block)
{
block(123);
}
在非ARC下則需要手動retain
一下,否則如果屬性被置空,本地變量就成了野指針了,如下代碼:
//非ARC
MyBlockType block = [self.myBlock retain];
if (block)
{
block(123);
}
[block release];
2.
2. 循環引用問題
循環引用是另一個使用Block時常見的問題。
在ARC下,由於__block
抓取的變量一樣會被Block retain
,所以必須用弱引用才可以解決循環引用問題,iOS 5之后可以直接使用__weak
,之前則只能使用__unsafe_unretained
了,__unsafe_unretained
缺點是指針釋放后自己不會置空。示例代碼:
//iOS 5之前可以用__unsafe_unretained
//__unsafe_unretained typeof(self) weakSelf = self;
__weak typeof(self) weakSelf = self;
self.myBlock = ^(int paramInt)
{
//使用weakSelf訪問self成員
[weakSelf anotherFunc];
};
在非ARC下,顯然無法使用弱引用,這里就可以直接使用__block
來修飾變量,它不會被Block所retain
的,參考代碼:
//非ARC
__block typeof(self) weakSelf = self;
self.myBlock = ^(int paramInt)
{
//使用weakSelf訪問self成員
[weakSelf anotherFunc];
};