ivar 被 block 捕获
Contents
Block 访问类成员变量循环引用
谈谈ivar的直接访问 这里看来的,他写的比较绕。总结来说为什么 block 中直接访问成员变量也会捕获 self。
直接的答案是,在使用成员变量时需要使用 self 作为基地址去找 ivar 的地址。
写个 Demo
#import "MyObj.h"
@interface MyObj ()
@property (nonatomic) NSUInteger haha;
@end
@implementation MyObj {
NSUInteger bu_haha;
}
- (void)handleIvar {
bu_haha += 10;
_haha += 20;
}
@end
(一)有多少成员变量就生成多少个全局变量
在这里就是这两个全局变量,这是用来存储偏移的。
extern "C" unsigned long OBJC_IVAR_$_MyObj$bu_haha;
extern "C" unsigned long OBJC_IVAR_$_MyObj$_haha;
(二)偏移值赋值
extern "C" unsigned long int OBJC_IVAR_$_MyObj$bu_haha __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct MyObj, bu_haha);
extern "C" unsigned long int OBJC_IVAR_$_MyObj$_haha __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct MyObj, _haha);
((used, section ("__DATA,__objc_ivar")))
语法指定了这俩全局变量具体的存储位置——数据区的 __objc_ivar
段,也就是 wuziqi 所说的 ivar 太多,包大小扛不住的问题。(不用这个 llvm 标记语法结果是一样的)。
__OFFSETOFIVAR__
是个宏用来取 _ivar_t
里面的偏移值字段
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
(三)objC 操作成员变量
有了以上的基础之后,就可以操作成员变量了。
static void _I_MyObj_handleIvar(MyObj * self, SEL _cmd) {
(*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObj$bu_haha)) += 10;
(*(NSUInteger *)((char *)self + OBJC_IVAR_$_MyObj$_haha)) += 20;
}
所以,当 invoke 中需要使用成员变量时它必然要使用 self,因此 self 必须捕获起来。