前言:
- 本篇文章將介紹以下幾個和實(shí)例變量
ivar相關(guān)的runtime函數(shù)的使用
id object_getIvar(id obj, Ivar ivar)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
Ivar class_getInstanceVariable(Class cls, const char *name)
1. id object_getIvar(id obj, Ivar ivar)
分析: Ivar,即InstanceVariable(實(shí)例變量)。runtime對該函數(shù)的說明為:
Reads the value of an instance variable in an object.
即獲取一個對象obj的實(shí)例變量ivar的值。要使用這個函數(shù),首先需要一個Ivar,我們使用class_copyIvarList函數(shù)獲取一個Ivar數(shù)組從而獲取一個Ivar,現(xiàn)在先看看class_copyIvarList函數(shù)是怎么使用的
2. Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
說明:該函數(shù)的作用是獲取傳入類的所有實(shí)例變量,返回的是實(shí)例變量數(shù)組
以UITextField類為例,代碼示例如下:
unsigned int outCount;
Ivar *ivars = class_copyIvarList([UITextField class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
}
free(ivars);
說明:由于ARC只適用于Foundation等框架,對于Core Foundation 和 runtime 等并不適用,所以在使用帶有copy、retain等字樣的函數(shù)或方法時需要手動釋放free()。
獲取到Ivar后可以利用 ivar_getName 函數(shù)獲取 Ivar 的名稱,用 ivar_getTypeEncoding 函數(shù)獲取 Ivar 的類型編碼,通過類型編碼就可以知道該 Ivar 是何種類型的。
關(guān)于類型編碼,這里稍微說一點(diǎn):
為了協(xié)助runtime系統(tǒng),編譯器用字符串為每個方法的返回值和參數(shù)的類型進(jìn)行了編碼,并把該編碼和方法選擇器進(jìn)行綁定。這種編碼方案在其他上下文環(huán)境中也是有用的,因此它可以使用編譯器指令@encode()進(jìn)行獲取。當(dāng)給定一種類型時,@encode()就會返回對這種類型進(jìn)行編碼后的字符串,類型可以是基本類型比如int、指針、結(jié)構(gòu)、并集、類的名稱等等,事實(shí)上,它們也都可以作為C語言sizeof()運(yùn)算符的參數(shù)。(每種基本類型和編碼的對應(yīng)關(guān)系詳見蘋果官方文檔以及這篇譯文)
整體代碼如下:
unsigned int outCount; // 1
Ivar *ivars = class_copyIvarList([UITextField class], &outCount); // 2
for (int i = 0; i < outCount; i++) { // 3
Ivar ivar = ivars[i]; // 4
const char *ivarName = ivar_getName(ivar); // 5
const char *ivarType = ivar_getTypeEncoding(ivar); // 6
NSLog(@"實(shí)例變量名為:%s 字符串類型為:%s", ivarName, ivarType); // 7
} // 8
free(ivars); // 9
打印結(jié)果如下:
runtime[6630:406123] 實(shí)例變量名為:_textStorage 字符串類型為:@"_UICascadingTextStorage"
runtime[6630:406123] 實(shí)例變量名為:_borderStyle 字符串類型為:q
runtime[6630:406123] 實(shí)例變量名為:_minimumFontSize 字符串類型為:d
runtime[6630:406123] 實(shí)例變量名為:_delegate 字符串類型為:@
runtime[6630:406123] 實(shí)例變量名為:_background 字符串類型為:@"UIImage"
runtime[6630:406123] 實(shí)例變量名為:_disabledBackground 字符串類型為:@"UIImage"
runtime[6630:406123] 實(shí)例變量名為:_clearButtonMode 字符串類型為:q
runtime[6630:406123] 實(shí)例變量名為:_leftView 字符串類型為:@"UIView"
runtime[6630:406123] 實(shí)例變量名為:_leftViewMode 字符串類型為:q
runtime[6630:406123] 實(shí)例變量名為:_rightView 字符串類型為:@"UIView"
// 省略大部分
runtime[6630:406123] 實(shí)例變量名為:_placeholderLabel 字符串類型為:@"UITextFieldLabel"
runtime[6630:406123] 實(shí)例變量名為:_suffixLabel 字符串類型為:@"UITextFieldLabel"
// 省略大部分
現(xiàn)在就獲取到了UITextField類的所有實(shí)例變量,包括私有的。
使用場景:
有了這些實(shí)例變量就可以更方便地更改UITextField了,比如更改UITextField的占位字體顏色,占位字體默認(rèn)的顏色偏向于亮灰色,如下圖:

由于
UITextField沒有提供直接修改占位字體顏色的屬性,現(xiàn)在我們用其他方法嘗試。從剛才打印的實(shí)例變量名稱可以推測實(shí)例變量_placeholderLabel是顯示占位字體的一個UILabel,現(xiàn)在驗(yàn)證一下,在剛才代碼的第9行后添加如下代碼:
// self.textField就是上圖顯示的UITextField
id value = [self.textField valueForKey:@"_placeholderLabel"]; // 10
NSLog(@"value class:%@, value superclass:%@", NSStringFromClass([value class]), NSStringFromClass([value superclass])); // 11
打印結(jié)果如下:
runtime[44760:4568971] value class:UITextFieldLabel, value superclass:UILabel
可以看到,實(shí)例變量_placeholderLabel的類和剛才用類型編碼獲取到的相同。
既然實(shí)例變量_placeholderLabel是UILabel子類的實(shí)例,那就可以根據(jù)UILabel的textColor屬性利用KVC對UITextField更改字體顏色了:
[self.textField setValue:[UIColor orangeColor] forKeyPath:@"_placeholderLabel.textColor"]; // 12
重新進(jìn)行后效果如下圖:

3. Ivar class_getInstanceVariable(Class cls, const char *name)
此時再回到第一個函數(shù)id object_getIvar(id obj, Ivar ivar)的使用方法,有了Ivar名稱后如果需要再用到該Ivar,就需要用到class_getInstanceVariable函數(shù),該函數(shù)是的作用是獲取指定類的指定名稱的實(shí)例變量。現(xiàn)在在剛才的代碼后添加如下代碼:
Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
id getIvar = object_getIvar(self.textField, ivar);
NSLog(@"%@", getIvar);
打印結(jié)果如下:
runtime[45217:4596711] <UITextFieldLabel: 0x7fe6e35e6ad0; frame = (0 0; 0 0); text = 'please input text'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7fe6e35e69d0>>
得到的即是對象self.textField的ivar名稱為_placeholderLabel的值。