Block与Self的循环引用
01:众所周知若self中引用了Block块,而此Block块中又引用了Self则会造成循环引用,需要提醒的是即使在你的block代码中没有显式地出现"self",也有可能出现循环引用!只要你在block里用到了self所拥有的东西就有可能导致循环引用。如下面的代码就会造成循环引用,测试发现BlockTest对象使用后没有正常释放。
@interface BlockTest()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) void(^myBlock)(void);
@end
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"BlockTest";
self.myBlock = ^{
NSLog(@"%@",_name);
};
self.myBlock();
}
return self;
}
-(void)dealloc
{
NSLog(@"dealloc");
#if __has_feature(objc_arc)
NSLog(@"ARC %s",__func__);
#else
[super dealloc];
NSLog(@"MRC %s",__func__);
#endif
}
若改成以下代码则可以正常释放:
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"BlockTest";
NSString* str = self.name;
self.myBlock = ^{
//NSLog(@"%@",_name);
NSLog(@"%@",str);
};
self.myBlock();
}
return self;
}
02:为了解决循环引用问题:
在ARC:可以使用
__weak BlockTest *weakSelf = self;
或者
__unsafe_unretained BlockTest *unsafeSelf = self;
MRC:
__block BlockTest *blockSelf = self;
使用__unsafe_unretained也可以。
注意:MRC中__block是不会引起retain;但在ARC中__block则会引起retain。ARC中应该使用__weak 或__unsafe_unretained 弱引用。__weak只能在iOS5以后使用。通过测试会发现,在ARC中加了__weak和__unsafe_unretained的变量引入后,BlockTest可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。
03:验证02结论的测试代码:
#import <Foundation/Foundation.h>
typedef void(^blockT)();
@interface BlockTest : NSObject
@property (nonatomic,copy) blockT block;
@end
//———————————————
#import "BlockTest.h"
@interface BlockTest()
@property (nonatomic,strong) NSString *name;
@end
@implementation BlockTest
- (instancetype)init
{
self = [super init];
if (self) {
__block typeof(self) weekSelf = self;
self.block = ^{
weekSelf.name = @"BlockTest";
NSLog(@"%@",weekSelf.name);
};
}
return self;
}
-(void)dealloc
{
NSLog(@"%s",__func__);
}
@end
测试:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
BlockTest *test = [[BlockTest alloc]init];
NSLog(@"%p",test);
}
通过类似的测试可以发现
MRC下面使用__unsafe_unretained和__block都可以避免这个问题(MRC下不能使用__weak关键字)。
ARC下使用__unsafe_unretained和__weak可以免。
04:看下面代码:
//MRC 环境下,下面的代码会崩溃,则至少可以说明MRC下,Block块对__block修饰的date没有进行保留,所以sleep(2.0)后,再使用date时造成了崩溃。
- (void)blockTest
{
__block NSDate *date = [NSDate date];
NSLog(@"%p %lu %@",date,[date retainCount],date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %lu %@",date,[date retainCount],date);
});
NSLog(@"%p %lu %@",date,[date retainCount],date);
}
//ARC 环境下,下面的代码编译时会报错,date不可以在block中修改。
- (void)blockTest
{
NSDate *date = [NSDate date];
NSLog(@"%p %@",date,date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date,date);
date = [NSDate date];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
//ARC 环境下,下面的代码不会报错也不会崩溃,date可以在block中修改,这至少可以说明sleep(2.0)后date依然存在。
- (void)blockTest
{
__block NSDate *date = [NSDate date];
NSLog(@"%p %@",date,date);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date,date);
date = [NSDate date];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
/*
2016-06-25 17:01:57.505 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:01:58.056 GCD[2125:284060] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:02:00.059 GCD[2125:284200] 0x7fab93c20c10 2016-06-25 09:01:56 +0000
2016-06-25 17:02:00.822 GCD[2125:284200] 0x7fab93eaeb80 2016-06-25 09:02:00 +0000
*/
-(void)dealloc
{
#if __has_feature(objc_arc)
#else
[super dealloc];
#endif
NSLog(@"%s",__func__);
}
//MRC,使用下面的方式就可以解决MRC下程序崩溃的问题。因为date2没有被__block修饰,所以其生命周期一直保留到block块执行结束。
- (void)blockTest
{
__block NSDate *date = [NSDate date];
NSLog(@"%p %@",date,date);
NSDate *date2 = date;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"%p %@",date2,date2);
date = [NSDate date];
NSLog(@"%p %@",date,date);
});
NSLog(@"%p %@",date,date);
}
05:下面来讨论一下用__block修饰普通的C变量会如何:
如果一个程序块在执行过程中访问任何“外部”变量,那么该程序块被创建时,会进行一些特殊的设置工作,以允许程序块访问那些变量。这些变量所包含的值要么被复制(如一些普通的C类型变量),要么被保存(如OC对象),这样他们所包含的值就可以在程序块内部使用了(被生命周期修饰符修饰的变量,要分ARC、MRC等具体讨论)。
但如果想让一个程序块向一个外部定义的变量写入数据,这时用__block存储修饰符修饰这个变量即可。一个有趣的副作用是:__block修饰的变量(在MRC中)在程序块中使用时不会被复制(看下面的例子会发现,普通的C类型变量会被复制到堆中)或者保留(如上面的示例程序,在MRC下Block修饰的变量并没有被保留)。
//ARC,下面的程序不会崩溃。
- (void)blockTest
{
int intt2 = 200;
NSLog(@"1:%p",&intt2);
__block int intt = 100;
NSLog(@"2:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"3:%p %i",&intt,intt);
intt = 200;
NSLog(@"4:%p %i",&intt,intt);
});
int *inta = malloc(sizeof(int));
NSLog(@"5:%p",inta);
free(inta);
NSLog(@"6:%p %i",&intt,intt);
}
2016-06-25 17:38:06.765 GCD[2742:304212] 1:0x7fff50a02a8c
2016-06-25 17:38:11.063 GCD[2742:304212] 2:0x7fff50a02a80 100 //可以看出intt、intt2这两个都是在栈上面分配的空间
2016-06-25 17:38:15.624 GCD[2742:304212] 5:0x7f93da408e80
2016-06-25 17:38:15.624 GCD[2742:304253] 3:0x7f93da535ef8 100
2016-06-25 17:38:17.211 GCD[2742:304212] 6:0x7f93da535ef8 100
2016-06-25 17:38:29.243 GCD[2742:304253] 4:0x7f93da535ef8 200 //可以看出intt被在堆中重新复制了一遍,甚至在block块外面其地址也与开始时不同(下面还会讨论这种情况何时才会发生)。
注意看下面的例子:
//MRC,下面的程序同样不会崩溃。
- (void)blockTest
{
int intt2 = 200;
NSLog(@"1:%p",&intt2);//这两个都是在栈上面分配的空间
__block int intt = 100;
NSLog(@"2:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"3:%p %i",&intt,intt);
intt = 200;
NSLog(@"4:%p %i",&intt,intt);
});
int *inta = malloc(sizeof(int));
NSLog(@"5:%p",inta);
free(inta);
NSLog(@"6:%p %i",&intt,intt);
}
2016-06-25 17:42:08.125 GCD[2857:307005] 1:0x7fff5dc39a8c
2016-06-25 17:42:19.124 GCD[2857:307005] 2:0x7fff5dc39a80 100
2016-06-25 17:42:30.435 GCD[2857:307005] 5:0x7fc782554960
2016-06-25 17:42:35.086 GCD[2857:307005] 6:0x7fc78240ab38 100
2016-06-25 17:42:35.086 GCD[2857:307395] 3:0x7fc78240ab38 100
2016-06-25 17:42:43.869 GCD[2857:307005] MRC -[BlockTest dealloc]
2016-06-25 17:42:46.275 GCD[2857:307395] 4:0x7fc78240ab38 200
MRC中intt同样在堆中被重新复制了一遍。结和self与block循环引用的讨论,可以得出如下结果:
MRC下 block块外面用__block修饰的变量,若是OC对象,则不会retain,C类型变量会在堆中重新分配
ARC下 block块外面用__block修饰的变量,若是OC对象,会retain,C类型变量会在堆中重新分配。
06://ARC 注意看内存分配,与__block修饰时还是有很大不同
- (void)blockTest2
{
int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"2:%p %i",&intt,intt);
//intt = 200;
NSLog(@"3:%p %i",&intt,intt);
});
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 17:59:46.987 GCD[3137:315124] 0x7f8beb693f30
2016-06-25 17:59:48.794 GCD[3137:315124] 1:0x7fff5b27ca8c 100
2016-06-25 17:59:48.795 GCD[3137:315124] 4:0x7fff5b27ca8c 100
2016-06-25 17:59:50.800 GCD[3137:315194] 2:0x7f8beb611820 100
2016-06-25 17:59:50.800 GCD[3137:315194] 3:0x7f8beb611820 100
用__block修饰时4与2和3打印的地址是相通的,没有其修饰时1与4相同,2与3相同。
再看MRC:
- (void)blockTest2
{
int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(2.0);
NSLog(@"2:%p %i",&intt,intt);
//intt = 200;
NSLog(@"3:%p %i",&intt,intt);
});
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 18:04:10.279 GCD[3234:317528] 1:0x7fff53c36a8c 100
2016-06-25 18:04:10.279 GCD[3234:317528] 4:0x7fff53c36a8c 100
2016-06-25 18:04:10.279 GCD[3234:317528] MRC -[BlockTest dealloc]
2016-06-25 18:04:12.284 GCD[3234:317641] 2:0x7ffa2bd03b40 100
2016-06-25 18:04:12.284 GCD[3234:317641] 3:0x7ffa2bd03b40 100
MRC下与ARC相识1、4相同,2、3相同
总结如下:对于C类型变量:在MRC中不能用__weak,__unsafe_unretained、__strong、__autoreleasing等修饰。
无论MRC还是ARC 若使用__block进行修饰,则只要在block块中使用该变量(如下面两个例子,只有使用__block修饰该变量,且在Block块中使用了该变量,即使没有调用该Block就会出现下面的描述),该变量会被在堆中重新复制,即使在block块之外也重新分配到了新的位置。
- (void)blockTest2
{
__block int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// sleep(2.0);
// NSLog(@"2:%p %i",&intt,intt);
// //intt = 200;
// NSLog(@"3:%p %i",&intt,intt);
// });
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 18:13:12.249 GCD[3411:322184] 1:0x7fff5aa8da88 100
2016-06-25 18:13:12.249 GCD[3411:322184] 4:0x7fff5aa8da88 100
2016-06-25 18:13:12.249 GCD[3411:322184] dealloc
2016-06-25 18:13:13.794 GCD[3411:322184] ARC -[BlockTest dealloc]
- (void)blockTest2
{
__block int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
dispatch_block_t block = ^{
sleep(2.0);
NSLog(@"2:%p %i",&intt,intt);
NSLog(@"3:%p %i",&intt,intt);
};
//dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 18:16:43.413 GCD[3491:324296] 1:0x7fff5c522a88 100
2016-06-25 18:16:44.593 GCD[3491:324296] 4:0x7fe1d3e8eb88 100
2016-06-25 18:16:44.593 GCD[3491:324296] dealloc
2016-06-25 18:16:44.594 GCD[3491:324296] ARC -[BlockTest dealloc]
07:看下面的代码
- (void)blockTest2
{
__block int intt = 100;
NSLog(@"1:%p %i",&intt,intt);
dispatch_block_t block = ^{
sleep(2.0);
NSLog(@"2:%p %i",&intt,intt);
NSLog(@"3:%p %i",&intt,intt);
NSLog(@"%@",self.name);
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
NSLog(@"4:%p %i",&intt,intt);
}
2016-06-25 19:34:39.947 GCD[3643:334264] 1:0x7fff5aaeba88 100
2016-06-25 19:34:39.948 GCD[3643:334264] 4:0x7fade1c083d8 100
2016-06-25 19:34:41.950 GCD[3643:334315] 2:0x7fade1c083d8 100
2016-06-25 19:34:41.950 GCD[3643:334315] 3:0x7fade1c083d8 100
2016-06-25 19:34:50.096 GCD[3643:334315] BlockTest
2016-06-25 19:34:50.097 GCD[3643:334315] dealloc
2016-06-25 19:34:50.097 GCD[3643:334315] ARC -[BlockTest dealloc]
观察发现block块中使用到了self但,self并没有持有block块,所以最后self还是正常释放了。
08: MyArry的实现见最后
//ARC
- (void)blockTest
{
MyArry *obj1 = [[MyArry alloc]init];
[obj1 addObject:@"0"];
#if __has_feature(objc_arc)
NSLog(@"1:%p %@",obj1,obj1);
#else
NSLog(@"1:%p %@ %lu",obj1,obj1,obj1.retainCount);
#endif
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
sleep(2.0);
[obj1 addObject:@"2"];
#if __has_feature(objc_arc)
NSLog(@"2:%p %@",obj1,obj1);
#else
NSLog(@"2:%p %@ %lu",obj1,obj1,obj1.retainCount);
#endif
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
#if __has_feature(objc_arc)
NSLog(@"3:%p %@",obj1,obj1);
#else
NSLog(@"3:%p %@ %lu",obj1,obj1,obj1.retainCount);
#endif
}
2016-06-25 20:10:58.879 GCD[4171:348666] 1:0x7fef43604620 (
0
)
2016-06-25 20:10:59.882 GCD[4171:348666] 3:0x7fef43604620 (
0,
1
)
2016-06-25 20:11:00.886 GCD[4171:348718] 2:0x7fef43604620 (
0,
1,
2
)
2016-06-25 20:11:00.886 GCD[4171:348718] MyArry
2016-06-25 20:11:00.887 GCD[4171:348718] ARC -[MyArry dealloc]
一切正常,obj1正常释放,block块执行也没有崩溃,说明ARC下面obj1被保存。
09:ARC:
- (void)record:(NSInteger)inte myArry:(MyArry*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
//ARC
- (void)blockTest
{
__block MyArry *obj1 = [[MyArry alloc]init];
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
obj1 = [[MyArry alloc]init];
[obj1 addObject:@"2"];
[self record:4 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
}
2016-06-25 20:20:55.569 GCD[4356:353057] 1 0x7fc112c88f30 (
0
)
2016-06-25 20:20:55.569 GCD[4356:353105] 2 0x7fc112c88f30 (
0,
1
)
2016-06-25 20:20:56.571 GCD[4356:353057] 5 0x7fc112c88f30 (
0,
1,
3
)
2016-06-25 20:20:57.572 GCD[4356:353105] 3 0x7fc112c88f30 (
0,
1,
3
)
2016-06-25 20:20:57.572 GCD[4356:353105] MyArry
2016-06-25 20:20:57.572 GCD[4356:353105] ARC -[MyArry dealloc] //在[self record:3 myArry:obj1];后面 obj1被重新赋值,原来指向的对象正常释放。
2016-06-25 20:20:57.573 GCD[4356:353105] 4 0x7fc112e1af80 (
2
)
2016-06-25 20:20:57.573 GCD[4356:353105] MyArry
2016-06-25 20:20:57.573 GCD[4356:353105] ARC -[MyArry dealloc]
10:ARC
- (void)record:(NSInteger)inte myArry:(MyArry*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
//ARC
- (void)blockTest
{
MyArry *obj0 = [[MyArry alloc]init];
__weak MyArry *obj1 = obj0;
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
}
2016-06-25 20:23:03.586 GCD[4394:354093] 1 0x7ff168c0f5c0 (
0
)
2016-06-25 20:23:03.587 GCD[4394:354151] 2 0x7ff168c0f5c0 (
0,
1
)
2016-06-25 20:23:04.587 GCD[4394:354093] 5 0x7ff168c0f5c0 (
0,
1,
3
)
2016-06-25 20:23:04.588 GCD[4394:354093] MyArry
2016-06-25 20:23:04.588 GCD[4394:354093] ARC -[MyArry dealloc]
2016-06-25 20:23:05.592 GCD[4394:354151] 3 0x0 (null) //注意看,ARC下,若使用__weak此处打印的时null,这正是我们预料的。
11:ARC
- (void)record:(NSInteger)inte myArry:(MyArry*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
//ARC
- (void)blockTest
{
MyArry *obj0 = [[MyArry alloc]init];
__unsafe_unretained MyArry *obj1 = obj0;
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
}
2016-06-25 20:24:23.913 GCD[4421:354788] 1 0x7fbdfbc134f0 (
0
)
2016-06-25 20:24:23.921 GCD[4421:354830] 2 0x7fbdfbc134f0 (
0,
1
)
2016-06-25 20:24:24.922 GCD[4421:354788] 5 0x7fbdfbc134f0 (
0,
1,
3
)
2016-06-25 20:24:24.922 GCD[4421:354788] MyArry
2016-06-25 20:24:24.923 GCD[4421:354788] ARC -[MyArry dealloc]
(lldb)
程序崩溃,ARC下,若使用__unsafe_unretained,则程序如预期的崩溃了。
12:ARC 本想用__autoreleasing修饰MyArry *obj1,结果直接编译错误。
13:下面看MRC:
- (void)record:(NSInteger)inte myArry:(MyArry*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
//MRC
- (void)blockTest
{
MyArry *obj1 = [[MyArry alloc]init];
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj1 release];
}
2016-06-25 20:28:29.522 GCD[4506:357617] 1 0x7f8fe2d8fa00 (
0
) 1
2016-06-25 20:28:29.523 GCD[4506:357657] 2 0x7f8fe2d8fa00 (
0,
1
) 2
2016-06-25 20:28:30.524 GCD[4506:357617] 5 0x7f8fe2d8fa00 (
0,
1,
3
) 2
2016-06-25 20:28:31.527 GCD[4506:357657] 3 0x7f8fe2d8fa00 (
0,
1,
3
) 1
2016-06-25 20:28:31.528 GCD[4506:357657] MyArry
2016-06-25 20:28:31.528 GCD[4506:357657] ARC -[MyArry dealloc] //一切正常,此处打印的ARC,是因为MyArry是用ARC管理的,但测试代码是MRC
14:MRC
- (void)record:(NSInteger)inte myArry:(MyArry*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
//MRC
- (void)blockTest
{
__block MyArry *obj1 = [[MyArry alloc]init];
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj1 release];
}
2016-06-25 20:31:30.899 GCD[4584:359488] 1 0x7ffd12d03220 (
0
) 1
2016-06-25 20:31:30.900 GCD[4584:359529] 2 0x7ffd12d03220 (
0,
1
) 1
2016-06-25 20:31:31.901 GCD[4584:359488] 5 0x7ffd12d03220 (
0,
1,
3
) 1
2016-06-25 20:31:31.902 GCD[4584:359488] MyArry
2016-06-25 20:31:31.902 GCD[4584:359488] ARC -[MyArry dealloc]
程序崩溃,如预料的一样,程序崩溃了。
15:下面看一下MRC下__unsafe_unretained修饰会如何
//MRC
- (void)blockTest
{
__unsafe_unretained MyArry *obj1 = [[MyArry alloc]init];
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj1 release];
}
本来预计会崩溃,实际上运转正常
2016-06-25 20:35:45.911 GCD[4646:361755] 1 0x7fb393e0ab20 (
0
) 1
2016-06-25 20:35:45.912 GCD[4646:361797] 2 0x7fb393e0ab20 (
0,
1
) 2
2016-06-25 20:35:46.913 GCD[4646:361755] 5 0x7fb393e0ab20 (
0,
1,
3
) 2
2016-06-25 20:35:47.917 GCD[4646:361797] 3 0x7fb393e0ab20 (
0,
1,
3
) 1
2016-06-25 20:35:47.917 GCD[4646:361797] MyArry
2016-06-25 20:35:47.918 GCD[4646:361797] ARC -[MyArry dealloc]
说明__unsafe_unretained不能用于MRC下解决self与block块的循环引用问题,因为他也会使引用计数+1(其实后面测试会发现,这是由于GCD引起的,__unsafe_unretained可以用于MRC下解决self与block块之间的循环引用问题);
———————————————————————
//
// MyArry.h
// GCD
//
// Created by ranzhou on 16/6/25.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MyArry : NSMutableArray
@end
———————————————————————
//
// MyArry.m
// GCD
//
// Created by ranzhou on 16/6/25.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import "MyArry.h"
#import <Foundation/NSArray.h>
@interface MyArry()
@property (nonatomic,strong) NSMutableArray *arry;
@end
@implementation MyArry
- (instancetype)initWithCapacity:(NSUInteger)numItems
{
self = [super initWithCapacity:numItems];
if (self) {
self.arry = [[NSMutableArray alloc]initWithCapacity:numItems];
}
return self;
}
- (instancetype)init
{
self = [super init];
if (self) {
self.arry = [[NSMutableArray alloc]init];
}
return self;
}
-(NSUInteger)count
{
return self.arry.count;
}
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index
{
[self.arry insertObject:anObject atIndex:index];
}
- (id)objectAtIndex:(NSUInteger)index
{
return [self.arry objectAtIndex:index];
}
-(NSString*)description
{
return [self.arry description];
}
- (void)dealloc
{
NSLog(@"%@",NSStringFromClass([self class]));
#if __has_feature(objc_arc)
NSLog(@"ARC %s",__func__);
#else
[super dealloc];
self.arry = nil;
NSLog(@"MRC %s",__func__);
#endif
}
@end
———————————————————————
16:再次验证15的结论:
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"BlockTest";//__strong __weak(编译错误) __autoreleasing
__unsafe_unretained typeof(self) anSelf = self;
self.myBlock = ^{
NSLog(@"%@",anSelf.name);
};
}
return self;
}
2016-06-25 20:46:26.378 GCD[4900:367521] <BlockTest: 0x7fac43c05860>
2016-06-25 20:46:26.379 GCD[4900:367521] dealloc
2016-06-25 20:46:26.379 GCD[4900:367521] MRC -[BlockTest dealloc]
__block 和__unsafe_unretained均有效果
17:很奇怪::
下面两个.m文件中的代码均为MRC环境:
//
// BlockTest.m
// GCD
//
// Created by ranzhou on 16/6/25.
// Copyright © 2016年 ranzhouee. All rights reserved.
//
#import "BlockTest.h"
@interface BlockTest()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) void(^myBlock)(void);
@end
@implementation BlockTest
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"BlockTest";//__strong __weak(编译错误) __autoreleasing
__unsafe_unretained typeof(self) anSelf = self;
NSLog(@"1:%lu",self.retainCount);
self.myBlock = ^{
NSLog(@"%@",anSelf.name);
NSLog(@"2:%lu",anSelf.retainCount);
};
NSLog(@"3:%lu",self.retainCount);
self.myBlock();
NSLog(@"4:%lu",self.retainCount);
}
return self;
}
-(void)dealloc
{
NSLog(@"dealloc");
#if __has_feature(objc_arc)
NSLog(@"ARC %s",__func__);
#else
[super dealloc];
NSLog(@"MRC %s",__func__);
#endif
}
- (void)blockTest2
{
BlockTest *obj0 = [[BlockTest alloc]init];
__unsafe_unretained BlockTest *obj1 = obj0;
//[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
//[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
sleep(1.0);
//[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj0 release];
}
- (void)record:(NSInteger)inte myArry:(NSObject*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
测试代码:
BlockTest *bloc = [[BlockTest alloc]init];
[bloc release];
[self blockTest2];
日志:
2016-06-25 21:04:15.713 GCD[5428:378544] 1:1
2016-06-25 21:04:15.714 GCD[5428:378544] 3:1
2016-06-25 21:04:15.714 GCD[5428:378544] BlockTest
2016-06-25 21:04:15.714 GCD[5428:378544] 2:1
2016-06-25 21:04:15.714 GCD[5428:378544] 4:1
2016-06-25 21:04:15.714 GCD[5428:378544] dealloc
2016-06-25 21:04:15.715 GCD[5428:378544] MRC -[BlockTest dealloc]
2016-06-25 21:04:15.715 GCD[5428:378544] 1:1
2016-06-25 21:04:15.715 GCD[5428:378544] 3:1
2016-06-25 21:04:15.715 GCD[5428:378544] BlockTest
2016-06-25 21:04:15.715 GCD[5428:378544] 2:1
2016-06-25 21:04:15.715 GCD[5428:378544] 4:1
2016-06-25 21:04:15.715 GCD[5428:378544] 1 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80> 1
2016-06-25 21:04:15.716 GCD[5428:378614] 2 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80> 2
2016-06-25 21:04:16.716 GCD[5428:378544] 5 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80> 2
2016-06-25 21:04:17.721 GCD[5428:378614] 3 0x7fc8e26bbf80 <BlockTest: 0x7fc8e26bbf80> 1
2016-06-25 21:04:17.721 GCD[5428:378614] dealloc
2016-06-25 21:04:17.721 GCD[5428:378614] MRC -[BlockTest dealloc]
很奇怪(注意看引用计数)。。。。
把上面的方法改一改:
- (void)blockTest2
{
BlockTest *obj0 = [[BlockTest alloc]init];
__unsafe_unretained BlockTest *obj1 = obj0;
//[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
//[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
block();
sleep(1.0);
//[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj0 release];
}
则:
2016-06-25 21:11:36.478 GCD[5544:382236] 1:1
2016-06-25 21:11:36.479 GCD[5544:382236] 3:1
2016-06-25 21:11:36.479 GCD[5544:382236] BlockTest
2016-06-25 21:11:36.479 GCD[5544:382236] 2:1
2016-06-25 21:11:36.479 GCD[5544:382236] 4:1
2016-06-25 21:11:36.479 GCD[5544:382236] dealloc
2016-06-25 21:11:36.480 GCD[5544:382236] MRC -[BlockTest dealloc]
2016-06-25 21:11:36.480 GCD[5544:382236] 1:1
2016-06-25 21:11:36.480 GCD[5544:382236] 3:1
2016-06-25 21:11:36.480 GCD[5544:382236] BlockTest
2016-06-25 21:11:36.480 GCD[5544:382236] 2:1
2016-06-25 21:11:36.480 GCD[5544:382236] 4:1
2016-06-25 21:11:56.287 GCD[5544:382236] 1 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240> 1
2016-06-25 21:11:56.897 GCD[5544:382236] 2 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240> 1
2016-06-25 21:12:00.566 GCD[5544:382236] 3 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240> 1
2016-06-25 21:12:03.358 GCD[5544:382236] 5 0x7ffe21e02240 <BlockTest: 0x7ffe21e02240> 1
2016-06-25 21:12:03.358 GCD[5544:382236] dealloc
2016-06-25 21:12:03.358 GCD[5544:382236] MRC -[BlockTest dealloc]
可能是由于使用GCD的缘故:
个人觉得因为使用了GCD,所有Block块可能在变量声明的作用域外面被使用,所以对block进行了retain操作。
也有可能是因为这个block块作为函数的参数被使用,所以block被retain:
验证:
18:
//MRC
- (void)blockTest1
{
MyArry *obj0 = [[MyArry alloc]init];
__unsafe_unretained MyArry * obj1 = obj0;
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
[self fun:block];
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
[obj0 release];
}
- (void)fun:(myBlock)block
{
//dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
/**
2016-06-25 21:25:03.481 GCD[5753:387861] 1 0x7fc728c25ad0 (
0
) 1
2016-06-25 21:25:03.482 GCD[5753:387915] 2 0x7fc728c25ad0 (
0,
1
) 2
2016-06-25 21:25:04.482 GCD[5753:387861] 5 0x7fc728c25ad0 (
0,
1,
3
) 2
2016-06-25 21:25:05.485 GCD[5753:387915] 3 0x7fc728c25ad0 (
0,
1,
3
) 1
2016-06-25 21:25:05.486 GCD[5753:387915] MyArry
2016-06-25 21:25:05.486 GCD[5753:387915] ARC -[MyArry dealloc]
*/
}
- (void)fun:(myBlock)block
中若直接调用block(),则打印的引用计数是正常的,果然和GCD有关系。
19:
下面这段代码在MRC下运行良好,而在ARC下则会崩溃:
- (void)record:(NSInteger)inte myArry:(NSObject*)obj1
{
#if __has_feature(objc_arc)
NSLog(@"%li %p %@",inte,obj1,obj1);
#else
NSLog(@"%li %p %@ %lu",inte,obj1,obj1,obj1.retainCount);
#endif
}
- (void)fun:(myBlock)block
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
- (void)blockTest1
{
MyArry *obj0 = [[MyArry alloc]init];
__unsafe_unretained MyArry * obj1 = obj0;
[obj1 addObject:@"0"];
[self record:1 myArry:obj1];
dispatch_block_t block = ^{
[obj1 addObject:@"1"];
[self record:2 myArry:obj1];
sleep(2.0);
[self record:3 myArry:obj1];
};
[self fun:block];
sleep(1.0);
[obj1 addObject:@"3"];
[self record:5 myArry:obj1];
#if __has_feature(objc_arc)
NSLog(@"ARC");
#else
[obj0 release];
NSLog(@"MRC");
#endif
}
20:
下面的这段代码不会引起循环引用,开头刚刚讨论过
- (instancetype)init
{
self = [super init];
if (self) {
self.name = @"BlockTest";
NSLog(@"1:%lu",self.retainCount);
NSString *str = self.name;
self.myBlock = ^{
NSLog(@"%@",str);
};
NSLog(@"3:%lu",self.retainCount);
self.myBlock();
NSLog(@"4:%lu",self.retainCount);
}
return self;
}
2016-06-25 21:34:57.999 GCD[6005:393794] 1:1
2016-06-25 21:34:58.000 GCD[6005:393794] 3:1
2016-06-25 21:34:58.000 GCD[6005:393794] BlockTest
2016-06-25 21:34:58.000 GCD[6005:393794] 4:1
2016-06-25 21:34:58.000 GCD[6005:393794] dealloc
2016-06-25 21:34:58.000 GCD[6005:393794] MRC -[BlockTest dealloc]
至此依然有很多疑问:
1:为什么声明block属性时要使用copy;
2:为什么MRC中用__unsafe_unretained修饰的变量在Block块中使用时没有崩溃(具体见上面的示例)。
3:程序是如何复制在block中使用的__block修饰的C类型变量的。
……