赞
踩
如果在Block中使用附有_ _strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用。
HPPerson *person = [[HPPerson alloc] init];
person.block = ^{
NSLog(@"person.age--- %d",person.age);
};
在上面代码中,person对象强持有block对象,在block语法中,block对象又强持有person对象,此时达成互相强持有,谁也无法释法谁,造成循环引用。
当造成block循环引用时编译器会检测出并发出警告
另外,如果block内没有使用self也会捕获self,引起循环引用
typedef void (^blk_t) (void); @interface HPPerson : NSObject { blk_t _block; int _age; } @end #import "HPPerson.h" @implementation HPPerson -(id)init { self = [super init]; _block = ^{NSLog(@"age = %d", _age);}; return self; } - (void)dealloc { NSLog(@"%s", __func__); } @end
这是因为虽然没有使用self,但使用了self对象中的结构体成员,因此也会捕获self。
int main(int argc, const char * argv[]) { @autoreleasepool { HPPerson *person = [[HPPerson alloc] init]; person.age = 10; __weak HPPerson *weakPerson = person; person.block = ^{ NSLog(@"person.age--- %d",weakPerson.age); }; NSLog(@"--------"); } return 0; }
编译完成之后是
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// block内部对weakPerson是弱引用
HPPerson *__weak weakPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
局部变量消失时候,对于HPPerson来说,只有一个弱指针指向它,那它就销毁,然后block也销毁。
int main(int argc, const char * argv[]) { @autoreleasepool { HPPerson *person = [[HPPerson alloc] init]; person.age = 10; __unsafe_unretained HPPerson *weakPerson = person; person.block = ^{ NSLog(@"person.age--- %d",weakPerson.age); }; NSLog(@"--------"); } return 0; }
编译完成之后是
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
HPPerson *__unsafe_unretained weakPerson;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
虽然__unsafe_unretained
可以解决循环引用,但是最好不要用,因为:
__weak
:不会产生强引用,指向的对象销毁时,会自动让指针置为nil__unsafe_unretained
:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变,会造成野指针int main(int argc, const char * argv[]) { @autoreleasepool { __block HPPerson *person = [[HPPerson alloc] init]; person.age = 10; person.block = ^{ NSLog(@"person.age--- %d",person.age); //这一句不能少 person = nil; }; // 必须调用一次 person.block(); NSLog(@"--------"); } return 0; }
使用_ _Block修饰符解决循环引用时,需要注意的点有:
如果不调用block对象时,会造成下面情况的循环引用:
因为block会对__block
产生强引用
__block HPPerson *person = [[HPPerson alloc] init];
person.block = ^{
NSLog(@"person.age--- %d",person.age);
//这一句不能少
person = nil;
};
person对象本身就对block是强引用
@property (copy, nonatomic) HPBlock block;
__block
对person产生强引用
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
//`__block`对person产生强引用
HPPerson *__strong person;
};
当执行完person = nil
时候,__block
解除对person的引用,进而,全都解除释放了。 但是必须调用person = nil
才可以,否则,不能解除循环引用
self
时,应该使用弱引用,这样即使Block持有self
的引用,也不会阻止self
被释放。nil
,因此在Block内部使用self
之前,需要检查它是否为nil
。self
为nil
而导致的崩溃,可以在Block的开始处使用强引用#import <UIKit/UIKit.h> typedef void(^blk_t)(void); @interface ViewController : UIViewController @property (nonatomic, strong) blk_t block; @property (nonatomic, copy) NSString *name; @end #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.name = @"Hello"; __weak typeof(self) weakSelf = self; self.block = ^(){ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@", strongWeak.name); }; self.block(); } @end
此时self持有block,block弱引用self,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。但该方法可能存在中途就释放掉的问题(手动延迟,可能需要调用self.name的时候name已经被释放了)如果self被销毁,那么block则无法获取name。
因此可以改进上面的代码:
self.name = @"Hello";
__weak typeof(self) weakSelf = self;
self.block = ^(){
__strong __typeof(weakSelf)strongWeak = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", strongWeak.name);
});
};
self.block();
在完成block中的操作之后,才调用了dealloc方法。添加strongWeak之后,持有关系为:self -> block -> strongWeak -> weakSelf -> self。
weakSelf被强引用了就不会自动释放,因为strongWeak只是一个临时变量,它的声明周期只在block内部,block执行完毕后,strongWeak就会释放,而弱引用weakSelf也会自动释放。
通过给block传参(指针拷贝)
// 循环引用
self.name = @"Hello";
self.block = ^(ViewController * ctrl){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", ctrl.name);
});
};
self.block(self);
// staticSelf_定义:
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。