文章目录
- OC对象 - Block解决循环引用
- 前言
- 1. 循环引用示例
- 1.1 分析
- 2. 解决思路
- 3. ARC下
- 3.1 __weak
- 3.2 __unsafe_unretained
- 3.3 __block
- 4. MRC下
- 4.1 __unsafe_unretain....
- 4.1 __block
- 5. 总结
- 5.1 ARC下
- 5.2 MRC下
OC对象 - Block解决循环引用
前言
本章将会通过一个循环引用
案例,分析其原因,以及在ARC
、MRC
下的解决方案
1. 循环引用示例
ZSXperson
类
@interface ZSXPerson : NSObject
@property (nonatomic, assign) int age;
@property (nonatomic, copy) void(^block)(void);
@end
@implementation ZSXPerson
- (void)dealloc {
NSLog(@"ZSXPerson - %s", __func__);
}
@end
main.m 中初始化ZSXperson
对象person
,创建person
的block
,并在block
里面访问person.age
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
person.block = ^ {
NSLog(@"person.age is %d", person.age);
};
NSLog(@"---------------");
}
return 0;
}
运行:
person
不会销毁
1.1 分析
- 初始化
ZSXperson
对象person
,然后将block
赋值给person
的block
,此时person
是强引用block
- 在
block
中,访问了person.age
,因为访问了局部变量,所以block
捕获了person
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 main.m -o main-arm64.cpp
- 此时
block
和person
的关系,是相互持有的关系
2. 解决思路
之所以叫循环引用
,其实就是相互之间的引用关系形成了环
解决方式就是,断开某个引用关系,打破这个环
3. ARC下
3.1 __weak
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
__weak typeof(person) weakPerson = person;
person.block = ^ {
NSLog(@"person.age is %d", weakPerson.age);
};
NSLog(@"---------------");
}
return 0;
}
person
正常销毁的
3.2 __unsafe_unretained
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
__unsafe_unretained typeof(person) uuPerson = person;
person.block = ^ {
NSLog(@"person.age is %d", uuPerson.age);
};
NSLog(@"---------------");
}
return 0;
}
使用__unsafe_unretained
也可以正常释放
__unsafe_unretained
从命名看,unsafe
代表不安全,unretained
表示不会产生引用。不安全
则表现在,当引用的对象销毁时,他不会把指针设置为nil
,因此容易有坏指针
异常
3.3 __block
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
person.age = 10;
__block typeof(person) strongPerson = person;
person.block = ^ {
NSLog(@"person.age is %d", strongPerson.age);
strongPerson = nil;
};
person.block();
NSLog(@"---------------");
}
return 0;
}
使用__block
正常销毁
需要注意的是,使用__block
必须调用block。因为使用__block
修饰后,在block
里面需要置空
,如果没有调用block,就没有置空
操作,它依然是循环引用状态无法销毁
未调用block:
调用block后:
4. MRC下
MRC
不支持__weak
改为MRC
4.1 __unsafe_unretain…
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
person.age = 10;
__unsafe_unretained typeof(person) weakPerson = person;
person.block = [^ {
NSLog(@"person.age is %d", weakPerson.age);
} copy];
NSLog(@"---------------");
[person release];
}
return 0;
}
正常销毁
4.1 __block
int main(int argc, const char * argv[]) {
@autoreleasepool {
ZSXPerson *person = [[ZSXPerson alloc] init];
person.age = 10;
__block typeof(person) weakPerson = person;
person.block = [^ {
NSLog(@"person.age is %d", weakPerson.age);
} copy];
NSLog(@"---------------");
[person release];
}
return 0;
}
正常销毁
MRC
下,block 里面无需将weakPerson
设置为nil
,也能正常释放。这是因为,MRC
下__block
不会对person
产生强引用(被__block修饰的对象类型,ARC时会retain,MRC时不会retain)
相当于这根线不生效,因此没有形成循环引用
5. 总结
5.1 ARC下
通常使用__weak
来解决循环引用。使用__weak
修饰的对象销毁后,指针会自动设置为nil
。__unsafe_unretained
和__block
虽然也可以解决环引用问题,但是__unsafe_unretained
在修饰的对象销毁后,指针不会自动设置为nil
,因此如果还去使用这个对象就会出现异常。__block
需要保证 block 有被调用,否则还是会循环引用。
5.2 MRC下
使用__unsafe_unretained
或者__block
@oubijiexi