Contents
  1. 1. Block 访问类成员变量循环引用

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 必须捕获起来。

Contents
  1. 1. Block 访问类成员变量循环引用