runtime使用篇: class_getIvarLayout 和 class_getWeakIvarLayout

前言:

本篇文章将介绍以下两个和 Ivar Layout 有关的函数:
const uint8_t *class_getIvarLayout(Class cls)
const uint8_t *class_getWeakIvarLayout(Class cls)

说明:

介绍Ivar Layout之前,我们先通过这篇回顾一下实例变量ivar,我们已经可以利用ivar_getTypeEncodingivar_getName两个函数获取到变量的类型和名称。平时我们常写的属性如下:

属性

变量类型和变量名称可以获取到了,但是strong或是weak却还不知道。本篇要介绍的这两个函数正是解决这个问题的。
这两个函数的返回值都是 const uint8_t * 类型,即uint8_t数组。uint8_t定义如下:

uint8_t

它本质上是unsigned char,一般是指无符号8位整型数。一个无符号8位整型数在16进制中是两位,恰好地,这两位中的后一位表示了连续的strong(或weak)类型的实例变量的数量,前一位表示连续的非strong(或weak)类型的实例变量的数量。这一句听着有些抽象,用代码示例来看看:

初窥:

我们先定义一个类并向类中添加少量的属性:

// Dog.h 文件
@interface Dog : NSObject

@property (nonatomic, strong) id property_1_s;
@property (nonatomic, weak) id property_2_w;
@property (nonatomic, unsafe_unretained) id property_3_un;
@property (nonatomic, weak) id property_4_w;
@property (nonatomic, strong) id property_5_s;
@property (nonatomic, strong) id property_6_s;
@property (nonatomic, unsafe_unretained) id property_7_un;
@property (nonatomic, strong) id property_8_s;
@property (nonatomic, strong) id property_9_s;
@property (nonatomic, weak) id property_10_w;
@property (nonatomic, weak) id property_11_w;
@property (nonatomic, strong) id property_12_s;

@end

// Dog.m 文件
@implementation Dog

@end

为了接下来打印时方便看是strongweakunsafe_unretained类型,在属性的最后加上了swun来区分。
现在将这两个函数用起来,代码示例如下:

// ViewController.m文件

printf("strong:\n");
const uint8_t *array_s = class_getIvarLayout([Dog class]);
int i = 0;
uint8_t value_s = array_s[i];
while (value_s != 0x0) {
    printf("\\x%02x\n", value_s);
    value_s = array_s[++i];
}

printf("----------\n");

printf("weak:\n");
const uint8_t *array_w = class_getWeakIvarLayout([Dog class]);
int j = 0;
uint8_t value_w = array_w[j];
while (value_w != 0x0) {
    printf("\\x%02x\n", value_w);
    value_w = array_w[++j];
}

Ivar Layout打印结果如下:

strong:
\x01
\x32
\x12
\x21
----------
weak:
\x11
\x11
\x52
\x10

通过打印结果解释刚才那句话:
先说class_getIvarLayout获取的结果:
第一个\x01表示一开始有0个非strong类型的实例变量,这是因为第一个属性property_1_sstrong类型的,这也正是第二位16进制那个数字1所表示的;
接下来有三个非strong类型的,两个strong类型的,即\x32;
接下来有一个非strong类型的,两个strong类型的,即\x12;
接下来有两个非strong类型的,一个strong类型的,即\x21;
这些正好对应了声明的所有的属性。
同理可以理解class_getWeakIvarLayout获取的结果。

进阶:

刚才在类中添加的属性都是8字节(64位系统下)的id类型的,现在再向类中添加如下几个其他类型的:

// 在刚才的property_12_s属性后添加
/** ----------我是分割线---------- */
@property (nonatomic, assign) int property_13_int;
@property (nonatomic, assign) short property_14_short;
@property (nonatomic, weak) id property_15_w;
@property (nonatomic, assign) char property_16_char;
@property (nonatomic, strong) id property_17_s;

现在属性一共是17个。运行程序后Ivar Layout打印结果如下:

strong:
\x11
\x32
\x12
\x21
\x11
----------
weak:
\x21
\x11
\x52
\x11
\x10

有没有发现,这两个函数获取到的Ivar Layout数字总和都是15,而不是属性的个数17?而且获取到的strong类型的第一位数字就和刚才不一样(本次\x11,刚才\x01)?我们现在用函数class_copyIvarList获取类的实例变量列表,看看能不能找出原因。在ViewController.m文件中添加如下代码:

unsigned int count;
Ivar *list = class_copyIvarList([Dog class], &count);
for (int i = 0; i < count; i++) {
    Ivar ivar = list[i];
    const char * ivarName = ivar_getName(ivar);
    NSLog(@"%s", ivarName);
}

本次打印结果如下:

runtime[10502:809019] _property_16_char
runtime[10502:809019] _property_14_short
runtime[10502:809019] _property_13_int
runtime[10502:809019] _property_1_s
runtime[10502:809019] _property_2_w
runtime[10502:809019] _property_3_un
runtime[10502:809019] _property_4_w
runtime[10502:809019] _property_5_s
runtime[10502:809019] _property_6_s
runtime[10502:809019] _property_7_un
runtime[10502:809019] _property_8_s
runtime[10502:809019] _property_9_s
runtime[10502:809019] _property_10_w
runtime[10502:809019] _property_11_w
runtime[10502:809019] _property_12_s
runtime[10502:809019] _property_15_w
runtime[10502:809019] _property_17_s

可以看到,一开始添加的id类型的12个属性,用函数class_copyIvarList获取到的顺序和自己写的顺序仍然一样;而后添加的其他类型的属性,在实例变量列表中的顺序和自己写的就不一样了。
用这个实例变量列表再和刚才的Ivar Layout对照--为了检验问题是不是由基本数据类型的属性产生的,我们可以倒序着对照(即从_property_17_s开始):

  • 先倒序对照strong类型的,一直到_property_1_s都没问题,_property_1_s对应的是第一个\x11中的后一位1;而_property_16_char_property_14_short_property_13_int三个实例变量共同对应前一位1
  • 再倒序对照weak类型的,一直到_property_2_w都没问题,_property_2_w对应的是第一个\x21中的后一位1;而_property_16_char_property_14_short_property_13_int三个实例变量和_property_1_s共同对应前一位2

现在已经可以推测出,问题正是由基本数据类型的属性引起的。可是为什么呢?
还记着这篇文章里说到的『字节对齐』吗?在上述实例变量列表中,charshortint类型的三个实例变量在『字节对齐』下占8位,和一个id类型的实例变量所占字节数相同,而且它们是基本数据类型,既不是strong也不是weak,因此刚才的Ivar Layout就可以理解了。
读者可以再自行添加几个基本数据类型的属性进行验证,本文不再赘述。

另外,当某个属性是copy类型时,Ivar Layout会把它当strong类型进行处理。
再者,将id类型改成NSString *UIView *等类型,获取结果也相同。

总结:

1. 这两个函数获取到的Ivar Layout是和class_copyIvarList函数获取到的实例变量列表对应的,但要注意『字节对齐』;
2. 经过测试发现,当一个类中没有strong类型的实例变量时,用class_getIvarLayout函数获取到的结果为NULL,这时如果再像代码示例中array_s[i]获取角标为0的uint8_t类型数据时,会直接crash。class_getWeakIvarLayout如是。所以严谨地应该先判断获取到的是否为空。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容