block是如何持有对象的
这篇文章主要会介绍 block 是如何持有并且释放对象的。文章中的代码都出自 Facebook 开源的用于检测循环引用的框架 FBRetainCycleDetector。
为什么会谈到 block
可能很多读者会有这样的疑问,本文既然是对 FBRetainCycleDetector 解析的文章,为什么会提到 block?原因其实很简单,因为在 iOS 开发中大多数的循环引用都是因为 block 使用不当导致的,由于 block 会 retain 它持有的对象,这样就很容易造成循环引用,最终导致内存泄露。
在 FBRetainCycleDetector 中存在这样一个类 FBObjectiveCBlock,这个类的 - allRetainedObjects 方法就会返回所有 block 持有的强引用,这也是文章需要关注的重点。
- (NSSet *)allRetainedObjects {
NSMutableArray *results = [[[super allRetainedObjects] allObjects] mutableCopy];
__attribute__((objc_precise_lifetime)) id anObject = self.object;
void *blockObjectReference = (__bridge void *)anObject;
NSArray *allRetainedReferences = FBGetBlockStrongReferences(blockObjectReference);
for (id object in allRetainedReferences) {
FBObjectiveCGraphElement *element = FBWrapObjectGraphElement(self, object, self.configuration);
if (element) {
[results addObject:element];
}
}
return [NSSet setWithArray:results];
}
这部分代码中的大部分都不重要,只是在开头调用父类方法,在最后将获取的对象包装成一个系列 FBObjectiveCGraphElement,最后返回一个数组,也就是当前对象 block 持有的全部强引用了。
Block 是什么?
对 block 稍微有了解的人都知道,block 其实是一个结构体,其结构大概是这样的:
struct BlockLiteral {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, 。。.);
struct BlockDeor *deor;
};
struct BlockDeor {
unsigned long int reserved;
unsigned long int size;
void (*copy_helper)(void *dst, void *src);
void (*dispose_helper)(void *src);
const char *signature;
};
在 BlockLiteral 结构体中有一个 isa 指针,而对 isa了解的人也都知道,这里的 isa 其实指向了一个类,每一个 block 指向的类可能是 __NSGlobalBlock__、__NSMallocBlock__ 或者 __NSStackBlock__,但是这些 block,它们继承自一个共同的父类,也就是 NSBlock,我们可以使用下面的代码来获取这个类:
static Class _BlockClass() {
static dispatch_once_t onceToken;
static Class blockClass;
dispatch_once(&onceToken, ^{
void (^testBlock)() = [^{} copy];
blockClass = [testBlock class];
while(class_getSuperclass(blockClass) && class_getSuperclass(blockClass) != [NSObject class]) {
blockClass = class_getSuperclass(blockClass);
}
[testBlock release];
});
return blockClass;
}
Objective-C 中的三种 block __NSMallocBlock__、__NSStackBlock__ 和 __NSGlobalBlock__ 会在下面的情况下出现:
在 ARC 中,捕获外部了变量的 block 的类会是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 __NSStackBlock__ 变成 __NSMallocBlock__;但是如果 block 没有被赋值给某个变量,那它的类型就是 __NSStackBlock__;没有捕获外部变量的 block 的类会是 __NSGlobalBlock__ 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。
在非 ARC 中,捕获了外部变量的 block 的类会是 __NSStackBlock__,放置在栈上,没有捕获外部变量的 block 时与 ARC 环境下情况相同。
非常好我支持^.^
(1) 100%
不好我反对
(0) 0%
下载地址
block是如何持有对象的下载
相关电子资料下载
- iOS17.1可能明天发布,iOS17.1主要修复哪些问题? 377
- LinkedBlockingQueue基于单向链表的实现 115
- BlockingQueue主要属性和构造函数 94
- 华为全新鸿蒙蓄势待发 仅支持鸿蒙内核和鸿蒙系统应用 719
- 苹果手机系统iOS 17遭用户质疑 731
- iPhone12辐射超标?苹果推送iOS 17.1解决此事 750
- 传华为囤积零部件 目标明年智能手机出货7000万部;消息称 MiOS 仅限国内,小米 28208
- 苹果推送iOS17.0.3,解决iPhone15Pro系列存在机身过热 216
- Testin云测兼容和真机服务平台中上线iPhone 15系列手机 208
- 利尔达推出搭载HooRiiOS的Matter模组 145