1.block類型
1.1__NSGlobalBlock__ :全局區(qū)的 (沒有引用外部變量)
應(yīng)用場景:提示語提示用戶或者保存數(shù)據(jù)等其他情況仅醇,不做邏輯操作
[Person personWithName:^(NSString *personName) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:personName delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}];
沒有使用外部變量荡碾,此時的 alert 變量是放在 棧區(qū) ,block執(zhí)行完即釋放,類似的情況還有AFNetWorking里failure:^(NSError *error){#提示語#} 不用擔(dān)心循環(huán)引用
1.2__NSStackBlock__ :棧區(qū) (內(nèi)部使用了外部變量)
應(yīng)用場景:做邏輯操作婉徘,賦值,遍歷數(shù)組或者動畫等
[Person personWithName:^(NSString *personName) {
self.name = personName;
NSLog(@“%@",self.name);
}];
使用了外部變量_name屬性,此時就成為了棧Block逮光,類似的還有-(void)enumerateObjectsUsingBlock:(void (^)(id _Nonnull, NSUInteger, BOOL * _Nonnull))block 和 +(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 都是 棧Block,不用擔(dān)心循環(huán)引用墩划,下面我會來證實(shí)一下涕刚,像平時自己寫的方法中出現(xiàn)的block,都是棧block乙帮,不用擔(dān)心循環(huán)引用
1.3__NSMallocBlock__ :堆區(qū) (copy后Block存放在堆區(qū))
應(yīng)用場景:做邏輯操作杜漠,一般多見于屬性,@property (nonatomic,copy) void (^SelectPeoplesOK)(NSString *employees);或者是執(zhí)行[block copy]操作察净,然后賦值給另一個otherblock驾茴,那么使用otherblock的時候,就要注意循環(huán)引用了
Person *person = [[Person alloc] init];
__weak typeof(self) weakSelf = self;
person.personNameBlock = ^(NSString *personName) {
weakSelf.name = personName;
};
[person blockTest];
有的小伙伴可能覺得還需要在block里面執(zhí)行一下__strong __typeof(weakSelf)strongSelf = weakSelf; 其實(shí)這個例子不要氢卡,下面討論
2.如何定位Block類型
對于我自己锈至,我有兩個辦法,一是打斷點(diǎn)在block上面译秦,然后看后臺數(shù)據(jù)峡捡。或者是NSLog輸出block诀浪。只有定位對了block類型棋返,才能決定是否要考慮循環(huán)引用問題,不然就一直無腦用了雷猪。直接貼代碼睛竣,貼圖了。求摇。射沟。
#import typedef void(^PersonNameBlock)(NSString *personName);
@interface Person : NSObject
@property (nonatomic,copy) PersonNameBlock personNameBlock;
- (void)personWithName:(PersonNameBlock)personBlock;
- (void)blockTest;
- (void)test;
@end
#import "Person.h"
@implementation Person
- (void)personWithName:(PersonNameBlock)personBlock
{
NSLog(@"personBlock : %@",personBlock);
if (personBlock)
{
personBlock(@"張三");
}
}
- (void)blockTest
{
NSLog(@"personBlock : %@",self.personNameBlock);
if (self.personNameBlock)
{
self.personNameBlock(@"張三");
}
}
- (void)test
{
NSLog(@"xxxx");
}
@end
Person *person = [[Person alloc] init];
[person personWithName:^(NSString *personName) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:personName delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}];
3.png
4.png
5.png
6.png
3.什么時候block里面需要使用__strong typeof(weakSelf)strongSelf = weakSelf
其實(shí)答案簡單殊者,就是防止在執(zhí)行block的時候局部變量weakSelf已經(jīng)被釋放了,weakSelf ==nil验夯,那么執(zhí)行 weakSelf.屬性 的時候猖吴,就有問題了。 如果一直都寫挥转,大部分情況下是沒有問題的海蔽,但是要是深究下去,具體情況具體分析绑谣,那么部分情況是不需要寫的党窜。像上面的例子,就不需要寫strongSelf〗柘現(xiàn)在來一個必須要寫strongSelf的demo吧幌衣。。壤玫。
Person *person = [[Person alloc] init];
__weak Person * weakPerson = person;
person.personNameBlock = ^(NSString *personName) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakPerson test];
});
};
[person blockTest];
直接運(yùn)行這段代碼會發(fā)現(xiàn)[weakPerson test];并沒有執(zhí)行豁护,打印一下會發(fā)現(xiàn),weakPerson 已經(jīng)是 Nil 了欲间,這是由于當(dāng)我們的 viewDidLoad 方法運(yùn)行結(jié)束楚里,由于是局部變量,無論是 person 和 weakPerson 都會被釋放掉括改,那么這個時候在 Block 中就無法拿到正真的 person 內(nèi)容了腻豌。
正確寫法
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
__weak Person * weakPerson = person;
person.personNameBlock = ^(NSString *personName) {
__strong Person * strongPerson = weakPerson;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongPerson test];
});
};
[person blockTest];
}
4.自我總結(jié)
在使用block的時候,首先還是先確定block類型嘱能,然后看看自己的block里面有沒有用到“外部變量”,然后如果不確定是否需要block里寫
__strong XXX *xxx,打個斷點(diǎn)虱疏,跑一下代碼惹骂,看看__weak XXX *xxx會不會被釋放掉,久而久之做瞪,也就熟知自己經(jīng)常寫的block會不會有問題了对粪,個人見解,哪里寫的不對的装蓬,歡迎各位大佬指正批評著拭,謝謝~~