文章目录
- 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