runtime使用篇: object_getIvar、class_copyIvarList、ivar_getName、ivar_getTypeEncoding 和 class_getInstanceVariable

前言:
  • 本篇文章将介绍以下几个和实例变量ivar相关的runtime函数的使用
    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(实例变量)。runtime对该函数的说明为:
Reads the value of an instance variable in an object.
即获取一个对象obj的实例变量ivar的值。要使用这个函数,首先需要一个Ivar,我们使用class_copyIvarList函数获取一个Ivar数组从而获取一个Ivar,现在先看看class_copyIvarList函数是怎么使用的

2. Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

说明:该函数的作用是获取传入类的所有实例变量,返回的是实例变量数组
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 Foundationruntime 等并不适用,所以在使用带有copyretain等字样的函数或方法时需要手动释放free()
获取到Ivar后可以利用 ivar_getName 函数获取 Ivar 的名称,用 ivar_getTypeEncoding 函数获取 Ivar 的类型编码,通过类型编码就可以知道该 Ivar 是何种类型的。
关于类型编码,这里稍微说一点:

为了协助runtime系统,编译器用字符串为每个方法的返回值和参数的类型进行了编码,并把该编码和方法选择器进行绑定。这种编码方案在其他上下文环境中也是有用的,因此它可以使用编译器指令@encode()进行获取。当给定一种类型时,@encode()就会返回对这种类型进行编码后的字符串,类型可以是基本类型比如int、指针、结构、并集、类的名称等等,事实上,它们也都可以作为C语言sizeof()运算符的参数。(每种基本类型和编码的对应关系详见苹果官方文档以及这篇译文

整体代码如下:

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(@"实例变量名为:%s 字符串类型为:%s", ivarName, ivarType); // 7
} // 8
free(ivars); // 9

打印结果如下:

runtime[6630:406123] 实例变量名为:_textStorage 字符串类型为:@"_UICascadingTextStorage"
runtime[6630:406123] 实例变量名为:_borderStyle 字符串类型为:q
runtime[6630:406123] 实例变量名为:_minimumFontSize 字符串类型为:d
runtime[6630:406123] 实例变量名为:_delegate 字符串类型为:@
runtime[6630:406123] 实例变量名为:_background 字符串类型为:@"UIImage"
runtime[6630:406123] 实例变量名为:_disabledBackground 字符串类型为:@"UIImage"
runtime[6630:406123] 实例变量名为:_clearButtonMode 字符串类型为:q
runtime[6630:406123] 实例变量名为:_leftView 字符串类型为:@"UIView"
runtime[6630:406123] 实例变量名为:_leftViewMode 字符串类型为:q
runtime[6630:406123] 实例变量名为:_rightView 字符串类型为:@"UIView"
// 省略大部分
runtime[6630:406123] 实例变量名为:_placeholderLabel 字符串类型为:@"UITextFieldLabel"
runtime[6630:406123] 实例变量名为:_suffixLabel 字符串类型为:@"UITextFieldLabel"
// 省略大部分

现在就获取到了UITextField类的所有实例变量,包括私有的。

使用场景:

有了这些实例变量就可以更方便地更改UITextField了,比如更改UITextField的占位字体颜色,占位字体默认的颜色偏向于亮灰色,如下图:

默认的占位字体.png

由于UITextField没有提供直接修改占位字体颜色的属性,现在我们用其他方法尝试。从刚才打印的实例变量名称可以推测实例变量_placeholderLabel是显示占位字体的一个UILabel,现在验证一下,在刚才代码的第9行后添加如下代码:

// self.textField就是上图显示的UITextField
id value = [self.textField valueForKey:@"_placeholderLabel"]; // 10
NSLog(@"value class:%@, value superclass:%@", NSStringFromClass([value class]), NSStringFromClass([value superclass])); // 11

打印结果如下:

runtime[44760:4568971] value class:UITextFieldLabel, value superclass:UILabel

可以看到,实例变量_placeholderLabel的类和刚才用类型编码获取到的相同。
既然实例变量_placeholderLabelUILabel子类的实例,那就可以根据UILabeltextColor属性利用KVCUITextField更改字体颜色了:

[self.textField setValue:[UIColor orangeColor] forKeyPath:@"_placeholderLabel.textColor"]; // 12

重新进行后效果如下图:

修改后的占位字体.png
3. Ivar class_getInstanceVariable(Class cls, const char *name)

此时再回到第一个函数id object_getIvar(id obj, Ivar ivar)的使用方法,有了Ivar名称后如果需要再用到该Ivar,就需要用到class_getInstanceVariable函数,该函数是的作用是获取指定类的指定名称的实例变量。现在在刚才的代码后添加如下代码:

Ivar ivar = class_getInstanceVariable([UITextField class], "_placeholderLabel");
id getIvar = object_getIvar(self.textField, ivar);
NSLog(@"%@", getIvar);

打印结果如下:

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的值。

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

推荐阅读更多精彩内容