網(wǎng)上已經(jīng)有很多討論block底層實(shí)現(xiàn)原理的文章儒溉,這里不討論實(shí)現(xiàn)悄晃,只討論block使用中的一些問(wèn)題徽诲。
一轴咱、聲明block屬性時(shí)應(yīng)該使用什么關(guān)鍵字傻谁?
答案:copy
以下內(nèi)容翻譯自官方文檔:
定義一個(gè)跟蹤block的屬性的語(yǔ)法:
@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
注意:你應(yīng)該指定copy作為屬性關(guān)鍵字畸颅,因?yàn)閴K需要被拷貝以跟蹤它在原始范圍外捕獲的狀態(tài)狂芋。由于使用自動(dòng)引用計(jì)數(shù)時(shí)(Automatic Reference Counting)這是自動(dòng)發(fā)生的微谓,所以你不需要擔(dān)心這些纽门,但是屬性關(guān)鍵字的最佳做法是展示結(jié)果行為薛耻。更多關(guān)于block的信息,參閱Blocks Programming Topics赏陵。
上面這一段翻譯的官方文檔原文地址:Programming with Objective-C/Working with Blocks
注意:copy對(duì)屬性也是強(qiáng)引用饼齿,只不過(guò)這個(gè)強(qiáng)引用指向的是賦值給屬性的那個(gè)對(duì)象的一份拷貝。官方文檔copy關(guān)鍵字的使用有詳細(xì)說(shuō)明蝙搔,詳情參閱文檔《Programming with Objective-C》中Encapsulating Data主題的Copy Properties Maintain Their Own Copies章節(jié)
二缕溉、block循環(huán)引用
循環(huán)引用的產(chǎn)生
以下內(nèi)容翻譯自官方文檔:
block對(duì)任何捕獲的對(duì)象保持強(qiáng)應(yīng)用,包括self吃型,這意味著如果一個(gè)對(duì)象對(duì)一個(gè)捕獲了self的block保持了copy屬性(上面第一個(gè)問(wèn)題中解釋過(guò)证鸥,官方文檔建議block屬性使用copy關(guān)鍵字,且copy屬性self對(duì)屬性也是強(qiáng)引用,詳情請(qǐng)翻看問(wèn)題一):
@interface XYZBlockKeeper : NSObject
@property (copy) void (^block)(void);
@end
@implementation XYZBlockKeeper
- (void)configureBlock {
self.block = ^{
[self doSomething];// 捕獲對(duì)self的強(qiáng)引用(capturing a strong reference to self)
// 創(chuàng)建了一個(gè)強(qiáng)引用循環(huán)(creates a strong reference cycle)
};
}
...
@end
解決方法:
避免這個(gè)問(wèn)題的最好方法是捕獲對(duì)self的弱引用枉层,如下:
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // 捕獲弱引用以避免循環(huán)引用(capture the weak reference to avoid the reference cycle)
}
}
通過(guò)捕獲指向self的弱指針泉褐,block就不會(huì)與XYZBlockKeeper對(duì)象保持強(qiáng)關(guān)系。如果對(duì)象在block調(diào)用前解除分配鸟蜡,weakSelf指針將被簡(jiǎn)單地置為nil膜赃。
官方文檔地址:Programming with Objective-C/Working with Blocks
調(diào)用的方法中有block參數(shù),會(huì)不會(huì)導(dǎo)致循環(huán)引用矩欠?
問(wèn)題描述:如果self或self.property調(diào)用的方法中有個(gè)block參數(shù)财剖,在這個(gè)block參數(shù)中使用了self,會(huì)不會(huì)導(dǎo)致循環(huán)引用癌淮?
這里的方法其實(shí)有兩種情況:self中的方法和self中屬性對(duì)象中的方法
self自己的方法使用block作為參數(shù)躺坟,這個(gè)block中使用了self,會(huì)不會(huì)導(dǎo)致循環(huán)引用乳蓄?
代碼如下:
- (void)doSomething {
[self testWithBlock:^{
[self testMethod];
}];
}
- (void)testWithBlock:(void(^)())block {
block();
}
- (void)testMethod {
NSLog(@"test");
}
答案:此時(shí)block只是一個(gè)臨時(shí)變量咪橙,self并沒(méi)有對(duì)其持有,所有不會(huì)產(chǎn)生循環(huán)引用虚倒。
self的屬性對(duì)象中的方法美侦,使用block作為參數(shù),block中使用了self魂奥,會(huì)不會(huì)產(chǎn)生循環(huán)引用菠剩?
這里也有兩種情況:1、屬性對(duì)象持有了傳入的block參數(shù) 2耻煤、屬性對(duì)象未持有傳入的block參數(shù)
1具壮、屬性對(duì)象持有了傳入的block參數(shù)
代碼如下:
#import <Foundation/Foundation.h>
typedef void (^BlockType)(void);
@interface AbcObject : NSObject
- (void)methodWithBlock:(BlockType)block;
@end
#import "AbcObject.h"
@interface AbcObject()
//不作為公有屬性,而是在對(duì)外方法接口中把Block傳進(jìn)來(lái)
@property (nonatomic, copy) BlockType block;
@end
@implementation AbcObject
- (void)methodWithBlock:(BlockType)block {
//self.block留待后面其他地方異步調(diào)用
//會(huì)產(chǎn)生循環(huán)引用
self.block = block;
//比如哈蝇,發(fā)起一個(gè)異步請(qǐng)求
//...
}
//比如是在一個(gè)請(qǐng)求的回調(diào)中執(zhí)行剛才保存的block
- (void)completionDelegate {
//調(diào)用剛才保存的block
if (self.block) {
self.block();
}
}
@end
#import "ViewController.h"
#import "AbcObject.h"
@interface ViewController ()
@property (nonatomic, strong) AbcObject *abcObject;
@property (nonatomic, copy) NSString *str;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.str = @"haha";
self.abcObject = [[AbcObject alloc] init];
[self.abcObject configurePersonBlock:^{
NSLog(@"printf str:%@", self.str);
//解決問(wèn)題的辦法是block中使用weakSelf
}];
]}
@end
如上面的例子棺妓,這里也有兩種情況,如果傳入abcObject對(duì)象的block參數(shù)被abcObject對(duì)象作為屬性存儲(chǔ)起來(lái)以備其他地方的異步調(diào)用炮赦,這種情況self持有了person怜跑,person又持有了傳入的block,block中又引用了self吠勘,間接產(chǎn)生了循環(huán)引用性芬。
2、屬性對(duì)象未持有傳入的block參數(shù)
如果上面的代碼改一下剧防,methodWithBlock中不用屬性持有傳入的block(其余代碼不變):
#import <Foundation/Foundation.h>
typedef void (^ BlockType)(void);
@interface AbcObject : NSObject
- (void)methodWithBlock:(BlockType)block;
@end
#import "AbcObject.h"
@interface AbcObject()
@end
@implementation AbcObject
- (void)methodWithBlock:(BlockType)block {
//做其他事情
//...
//不會(huì)產(chǎn)生循環(huán)引用
if (block) {
block();
}
}
@end
則不會(huì)產(chǎn)生循環(huán)引用批旺。