循环引用
下面代码会产生循环引用吗?
__weak typeof(self) weakSelf = self;
self.doWork1 = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
weakSelf.doWork2 = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doWork2();
};
self.doWork1();
显然是会的。原因在于strongSelf和doWork2产生了循环引用。
解决如下点击了解更多关于循环引用的问题和解决方案:
__weak typeof(self) weakSelf = self;
self.doWork1 = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
__weak typeof(strongSelf) weakSelf2 = strongSelf;
strongSelf.doWork2 = ^{
__strong typeof(weakSelf2) strongSelf2 = weakSelf2;
NSLog(@"%@", strongSelf2);
};
strongSelf.doWork2();
};
self.doWork1();
//或者
__weak typeof(self) weakSelf = self;
self.doWork1 = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.doWork2 = ^{
__strong typeof(weakSelf) strongSelf2 = weakSelf;
NSLog(@"%@", strongSelf2);
};
strongSelf.doWork2();
};
self.doWork1();
作用域问题
下面的self.mArray和arr分别打印出什么?
- (void)test
{
NSMutableArray *arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
self.mArray = arr;
void (^block1)(void) = ^{
[arr addObject:@"3"];
[self.mArray addObject:@"a"];
NSLog(@"arr: %@",arr);
NSLog(@"mArray: %@",self.mArray);
};
[arr addObject:@"4"];
[self.mArray addObject:@"5"];
arr = nil;
self.mArray = nil;
block1();
}
arr正常打印,self.mArray打印为空,原因在于外面的变量arr和blcok里的arr作用域不一样,不是同一个arr,外面修改无效。而self.mArray的作用域是整个类的,自然包括block中。
block copy操作
1、这段程序能否正常运行?
//block1也被释放
NSObject *a = [NSObject alloc];
void(^__weak block1)(void) = nil;
{
void(^block2)(void) = ^{
NSLog(@"---%@", a);
};
block1 = block2;
NSLog(@"1 - %@ - %@",block1,block2);
}
block1();
不能。block1 = block2没有发生copy操作,引用计数没有增加,block1和block2指向的是同一个block,当离开了作用域block2被释放,block1也跟着被释放。
2、下面的代码能否正常运行?
struct _CustomBlock {
void (*invoke)(void);
};
- (void)customBlock
{
int a = 0;
void(^ __weak weakBlock)(void) = ^{
NSLog(@"-----%d", a);
};
//这里不发生copy操作,blc指向weakBlock
struct _CustomBlock *blc = (__bridge struct _CustomBlock *)weakBlock;
//copy到堆区,strongBlock已经是新的block
id __strong strongBlock = [weakBlock copy];
//原先的weakBlock的invoke置为nil不影响strongBlock
blc->invoke = nil;
//这里strongBlock已经是堆区的block,不再发生copy
void(^strongBlock1)(void) = strongBlock;
strongBlock1();
}
显然是可以运行的。原因是blc=weakBlock
时没有发生copy操作,blc指向的是weakBlock
,而在执行strongBlock = [weakBlock copy]
时才发生copy,此时strongBlock
指向的是新的copy到堆区的block,因此在执行blc->invoke = nil
只是将原来的block的invoke置为空,不影响新copy出来的block。这个demo说明了两个问题:1、weak修饰的block直接赋值是不会发生copy的;2、未发生过copy的block如果发生了copy操作就会生成新的block并copy到堆区。
__block的引用和copy & 普通对象的引用和copy 的比较
普通对象的引用和copy
/**1、创建对象objc1*/
NSObject *objc1 = [NSObject new];
NSLog(@"%---ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));//1
/**2、block1 引用对象objc1,引用计数+1, 发生copy操作引用计数再+1*/
void(^block1)(void) = ^{//+2
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));
};
block1();//打印的引用计数为3
/**3、block2 引用对象objc1,block2是weak变量,赋值不发生copy*/
void(^__weak block2)(void) = ^{//+1
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc1)));
};
block2();//打印的引用计数为4
/**4、block2发生copy操作,所以objc1引用计数+1*/
void(^block3)(void) = [block2 copy];//+1
block3();//打印的引用计数为5
/**5、block3虽然调用copy方法,但是此处copy未生效,原因是block3是block2执行copy出来的,在底层如果是已经copy到堆区的block再次进行copy的时候是不会生效的,所以objc1引用不会增加, 引用计数依然为5*/
void(^block4)(void) = [block3 copy];//+0
block4();//打印的引用计数为5
1、创建对象objc1,此时objc1的引用计数为1;
2、这里block1引用objc1对象,使得objc1引用计数加1。由于block1产生copy操作的同时会对objc1进行copy,所以引用计数再一次加1,所以为3;
3、这里block2引用objc1对象,使得objc1引用计数加1。由于block2是weak对象,没有发生copy操作,所以objc1引用计数没有在加1,所以为4;
4、由于block2发生copy操作的同时会对objc1进行copy,所以引用计数再一次加1,所以为5;
5、block3虽然调用copy方法,但是此处copy未生效,原因是block3是block2执行copy出来的,在底层如果是已经copy到堆区的block再次进行copy的时候是不会生效的,所以objc1引用不会增加, 引用计数依然为5;
__block的引用和copy
/**6、创建__block修饰的对象objc2, objc2引用计数为1*/
__block NSObject *objc2 = [NSObject new];
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));//1
/**7、block5引用objc2,objc2引用计数为1*/
void(^block5)(void) = ^{//+0
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));//1
};
block5();//打印的引用计数为1
/**8、block6引用objc2,objc2引用计数为1*/
void(^__weak block6)(void) = ^{//+0
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc2)));
};
block6();//打印的引用计数为1
/**9、block6发生copy操作,objc2引用计数为1*/
void(^block7)(void) = [block6 copy];//+0
block7();//打印的引用计数为1
6、创建__block修饰的对象objc2, objc2引用计数为1;
7、block5引用objc2,objc2引用计数为1;
8、block6引用objc2,objc2引用计数为1;
9、block6发生copy操作,objc2引用计数为1.
这里因为objc2是__block修饰的对象,所以block4引用的是对象objc2的指针,而不是引用objc2指向的对象,因此不管多少个block引用objc2,进行多少次copy,objc2引用计数不变。点击了解更多Block底层原理。
网友评论