前言:
本篇文章將介紹以下兩個和 Ivar Layout
有關(guān)的函數(shù):
const uint8_t *class_getIvarLayout(Class cls)
const uint8_t *class_getWeakIvarLayout(Class cls)
說明:
介紹Ivar Layout
之前舒憾,我們先通過這篇回顧一下實例變量ivar
,我們已經(jīng)可以利用ivar_getTypeEncoding
和ivar_getName
兩個函數(shù)獲取到變量的類型和名稱。平時我們常寫的屬性如下:
變量類型和變量名稱可以獲取到了聂喇,但是strong
或是weak
卻還不知道玄柏。本篇要介紹的這兩個函數(shù)正是解決這個問題的。
這兩個函數(shù)的返回值都是 const uint8_t *
類型饥臂,即uint8_t
數(shù)組街佑。uint8_t
定義如下:
它本質(zhì)上是unsigned char
,一般是指無符號8位整型數(shù)屹徘。一個無符號8位整型數(shù)在16進(jìn)制中是兩位走趋,恰好地,這兩位中的后一位表示了連續(xù)的strong
(或weak
)類型的實例變量的數(shù)量噪伊,前一位表示連續(xù)的非strong
(或weak
)類型的實例變量的數(shù)量簿煌。這一句聽著有些抽象,用代碼示例來看看:
初窺:
我們先定義一個類并向類中添加少量的屬性:
// 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
為了接下來打印時方便看是strong
鉴吹、weak
或unsafe_unretained
類型姨伟,在屬性的最后加上了s
、w
拙寡、un
來區(qū)分授滓。
現(xiàn)在將這兩個函數(shù)用起來,代碼示例如下:
// 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
打印結(jié)果如下:
strong:
\x01
\x32
\x12
\x21
----------
weak:
\x11
\x11
\x52
\x10
通過打印結(jié)果解釋剛才那句話:
先說class_getIvarLayout
獲取的結(jié)果:
第一個\x01
表示一開始有0個非strong
類型的實例變量肆糕,這是因為第一個屬性property_1_s
是strong
類型的般堆,這也正是第二位16進(jìn)制那個數(shù)字1
所表示的;
接下來有三個非strong
類型的诚啃,兩個strong
類型的淮摔,即\x32
;
接下來有一個非strong
類型的,兩個strong
類型的始赎,即\x12
;
接下來有兩個非strong
類型的和橙,一個strong
類型的,即\x21
;
這些正好對應(yīng)了聲明的所有的屬性造垛。
同理可以理解class_getWeakIvarLayout
獲取的結(jié)果魔招。
進(jìn)階:
剛才在類中添加的屬性都是8字節(jié)(64位系統(tǒng)下)的id
類型的,現(xiàn)在再向類中添加如下幾個其他類型的:
// 在剛才的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;
現(xiàn)在屬性一共是17個五辽。運行程序后Ivar Layout
打印結(jié)果如下:
strong:
\x11
\x32
\x12
\x21
\x11
----------
weak:
\x21
\x11
\x52
\x11
\x10
有沒有發(fā)現(xiàn)办斑,這兩個函數(shù)獲取到的Ivar Layout
數(shù)字總和都是15,而不是屬性的個數(shù)17杆逗?而且獲取到的strong
類型的第一位數(shù)字就和剛才不一樣(本次\x11
乡翅,剛才\x01
)?我們現(xiàn)在用函數(shù)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);
}
本次打印結(jié)果如下:
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個屬性悔橄,用函數(shù)class_copyIvarList
獲取到的順序和自己寫的順序仍然一樣靶累;而后添加的其他類型的屬性腺毫,在實例變量列表中的順序和自己寫的就不一樣了。
用這個實例變量列表再和剛才的Ivar Layout
對照--為了檢驗問題是不是由基本數(shù)據(jù)類型的屬性產(chǎn)生的挣柬,我們可以倒序著對照(即從_property_17_s
開始):
- 先倒序?qū)φ?code>strong類型的拴曲,一直到
_property_1_s
都沒問題,_property_1_s
對應(yīng)的是第一個\x11
中的后一位1
凛忿;而_property_16_char
、_property_14_short
和_property_13_int
三個實例變量共同對應(yīng)前一位1
竞川。 - 再倒序?qū)φ?code>weak類型的店溢,一直到
_property_2_w
都沒問題,_property_2_w
對應(yīng)的是第一個\x21
中的后一位1
委乌;而_property_16_char
床牧、_property_14_short
和_property_13_int
三個實例變量和_property_1_s
共同對應(yīng)前一位2
。
現(xiàn)在已經(jīng)可以推測出遭贸,問題正是由基本數(shù)據(jù)類型的屬性引起的戈咳。可是為什么呢壕吹?
還記著這篇文章里說到的『字節(jié)對齊』嗎著蛙?在上述實例變量列表中,char
耳贬、short
踏堡、int
類型的三個實例變量在『字節(jié)對齊』下占8位,和一個id
類型的實例變量所占字節(jié)數(shù)相同咒劲,而且它們是基本數(shù)據(jù)類型顷蟆,既不是strong
也不是weak
,因此剛才的Ivar Layout
就可以理解了腐魂。
讀者可以再自行添加幾個基本數(shù)據(jù)類型的屬性進(jìn)行驗證帐偎,本文不再贅述。
另外蛔屹,當(dāng)某個屬性是copy
類型時削樊,Ivar Layout
會把它當(dāng)strong
類型進(jìn)行處理。
再者判导,將id
類型改成NSString *
嫉父、UIView *
等類型,獲取結(jié)果也相同眼刃。
總結(jié):
1. 這兩個函數(shù)獲取到的Ivar Layout
是和class_copyIvarList
函數(shù)獲取到的實例變量列表對應(yīng)的绕辖,但要注意『字節(jié)對齊』;
2. 經(jīng)過測試發(fā)現(xiàn)擂红,當(dāng)一個類中沒有strong
類型的實例變量時仪际,用class_getIvarLayout
函數(shù)獲取到的結(jié)果為NULL
围小,這時如果再像代碼示例中array_s[i]
獲取角標(biāo)為0的uint8_t
類型數(shù)據(jù)時,會直接crash树碱。class_getWeakIvarLayout
如是肯适。所以嚴(yán)謹(jǐn)?shù)貞?yīng)該先判斷獲取到的是否為空。