runtime的零散笔记整理

整个程序的运行都是建立在runtime的运行时上。

runtime运行时是动态获取代码,也就是程序启动后获取代码。

所以,基于此原理可以做很多操作:
  · 给延展类生成属性(动态设置属性,动态获取属性。)
  · 方法的交换
  · 动态的归档解档
  · 字典转模型(如YYModel)

而runtime的运用其实就是一个个方法与属性的运用,只是用的不多。

我这里有简单的Demo,也有复杂的Demo,而Demo里附有我的讲解。

给延展类生成属性

为了给延展类生成属性,需要运用关联对象方法。(<objc/runtime.h>),

objc_get/set。 总共也就几行代码。

关联对象
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
以给定的键和策略为某对象设置关联对象值

id objc_getAssociatedObject(id object,void *key)
根据给定的键从某对象中获取对应的关联对象值

void objc_removeAssociatedObjects(id object)
移除指定对象的全部关联对象

目的是设置一个属性值为某对象,动态与某延展类对象关联。

还可用于给一个对象设置一个block值,在需要的时候,动态获取该对象的该block而后执行。

方法的交换
其实就三行代码,获取A对象的方法选择子a,获取B对象的方法选择子b,然后交换实现子。

//获取两个类的类方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));

Method m2 = class_getClassMethod([UIImage class], @selector(AD_imageNamed:));

//开始交换方法的实现
method_exchangeImplementations(m1, m2);
动态的归档解档

也就几行代码,获取对象的所有成员变量。然后归档(encodeObject:forKey:)解档(decodeObjectForKey:)

成员变量获取:class_copyIvarList(class,&int),返回是Ivar类型数值,里面像一个数组一样包含一堆数据,以及每个数据对应的一些数据。

可通过for循环遍历,遍历时可以通过ivar_getName(ivar)获取变量名。
在YYModel中就是用此方法获取到类的变量,进而进行键值匹配

字典转模型(如YYModel)

简单点的动态获取成员变量就可以了。如之前提到的:class_copyIvarList(class,&int)

复杂点的就是动态获取方法的选择子,用于实现属性的getter、setter方法
动态获取类的成员变量,通过偏移量得知成员变量的属性类型。如:字符串,int等。
这里面还会牵扯到元类、isa指针、字典的底层CFMutableDictionary等。

详情可看我的ADModel


objc_msgSend

OC消息传递机制中选择子发送的一种方式。可表示当前对象发送的选择子,且没有结构体返回值。

列如:
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);

这里的意思是,通过objc_msgSend给id类型的model对象发送一个选择子meta,选择子调用的方法所需参数为一个bool类型的值num.boolValue。而这个方法无返回值。

通俗点说就是让对象model去执行方法meta->_setter,方法所需参数是num.bollValue

再通俗点描述: ((void (*)(id, SEL, bool))(void *) objc_msgSend) 一位一个无返回值的函数指针,指向id的SEL方法,SEL方法所需参数是bool类型,使用objc_msgSend完成这个id调用SEL方法传递参数bool类型,(void *)objc_msgSend为什么objc_msgSend前加一个(void *)呢?我查了众多资料,众多。最后终于皇天不负有心人有了个结果,是为了避免某些错误,比如model对象的内存被意外侵占了、model对象的isa是一个野指针之类的。要是有大牛能说明白,麻烦跟我说下,谢谢了。

而((id)model, meta->_setter, num.boolValue)则一一对应前面的id,SEL,bool

选择子简单说就是@selector(),OC会提供一张选择子表供其查询,查询得到就去调用(通过实现子IMP),查询不到就添加而后查询对应的实现函数。通过_class_lookupMethodAndLoadCache3(仅提供给派发器用于方法查找的函数),其内部会调用lookUpImpOrForward方法查找,查找之后还会有初始化枷锁缓存之类的操作,详情请自行搜索,就不赘述了。

objc_msgSend,这个函数将消息接收者和方法名作为基础参数。消息发送给一个对象时,objc_msgSend通过对象的isa指针获得类的结构体,先在Cache里找,找到就执行,没找到就在分发列表里查找方法的selector,没找到就通过objc_msgSend结构体中指向父类的指针找到父类,然后在父类分发列表找,直到root class(NSObject)。

在64位下,直接使用objc_msgSend一样会引起崩溃,必须进行一次强转
((void(*)(id, SEL,int))objc_msgSend)(self, @selector(doSomething:), 0);
调用无参数无返回值方法
((void (*)(id, SEL))objc_msgSend)((id)msg, @selector(noArgumentsAndNoReturnValue));

在此方法里会先判断方法是否存在,若不存在返回nil,若存在则保存方法method,与方法操作SEL(method_getName(method)),方法实现IMP(method_getImplementation(method)),方法操作的名字( const char *name = sel_getName(_sel),返回值是一个值不可变的字符指针name,字符指针通常指向一个字符数组的首地址,字符数组类似于字符串。好吧,其实通常来说就是字符串(char [] 或 char * 与 String)。) 。


CFMutableDictionary * cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

CFMutableDictionary创建中四个参数意义为:

@功能 CFDictionaryCreateMutable创建一个新的词典。

@参数 CFAllocator 分配器应该用于分配CFAllocator字典的内存及其值的存储。这 参数可能为空,在这种情况下,当前的默认值CFAllocator使用。如果这个引用不是一个有效的cfallocator,其行为是未定义的。这里是内存及其值的存储为默认。

@参数 capacity 暗示值得个数,通过0实现可能忽略这个提示,或者可以使用它来优化各种。这里是忽略。

@参数 keyCallBacks 指向CFDictionaryKeyCallBacks结构为这本字典使用回调函数初始化在字典中的每一个键,初始化规则太多而且看的有点迷糊就不多说了,毕竟不敢乱说。意思应该是对键值对中的键进行初始化,并有相应的优化方式。

@参数 valueCallBacks 指向CFDictionaryValueCallBacks结构为这本词典使用回调函数初始化字典中的每一个值。对键值对中的值进行初始化,并有相应地优化方式。

其实按照ADModel中那个格式来就可以。


关于method

取得方法参数和返回值类型(method_getTypeEncoding(method))取得后通过UTF-8编码后保存。

方法的返回值类型(method_copyReturnType(method))取得后通过UTF-8编码后保存。

方法的参数个数(method_getNumberOfArguments(method))若个数大于0,则创建一个可变数组,而后去遍历获取参数类型(method_copyArgumentType(method, i)获取方法的指定位置参数的类型字符串),若获取到的参数类型存在就通过UTF-8编码后获取,不存在就为nil。


getClass
object_getClass(self) 返回对象的类,

objc_getClass(self);返回对象的isa指针,isa是一个指针指向对象自身,确切说是指向其元类,元类指向其元类,继承体系由此构成

iVar
类的成员变量获取:class_copyIvarList(class,&int),返回是Ivar类型数值,里面像一个数组一样包含一堆数据,以及每个数据对应的一些数据。

可通过for循环遍历,遍历时可以通过ivar_getName(ivar)获取变量名。

获取成员变量名(ivar_getName(ivar)),获取得到通过UTF-8编码后保存,在获取成员变量首地址的偏移量(ivar_getOffset(ivar)),runtime会计算ivar的地址偏移来找ivar的最终地址,获取成员变量类型编码(ivar_getTypeEncoding(ivar)),若获取得到通过UTF-8编码后保存。

编码后得到的是一个结构体,其中name与Value值对应如下:
属性类型 name值:T value:变化
编码类型 name值:C(copy) &(strong) W(weak) 空(assign) 等 value:无
非/原子性 name值:空(atomic) N(Nonatomic) value:无
变量名称 name值:V value:变化
属性描述为 T@"NSString",&,V_str 的 str
属性的描述:T 值:@"NSString"
属性的描述:V 值:_str2


为何给nil对象发消息不会崩,给对象发找不到的方法会崩?

给nil发消息,在传递给 objc_msgSend 中 self 参数是 nil,该函数会直接返回0表示空值,代表操作无意义。

给不为空的实例对象发送消息,会通过消息查找机制去依次调用三个系统方法,在最后一个方法里,系统会把消息与对象的相关数据统一处理,然后询问有人能否处理,如果没有,就抛异常代表程序崩溃。

而消息传递时调用的这三个方法也是可以重写的,而后做出一些拦截处理的操作。具体的方法操作,之后的文章中会描述。

程序崩溃不崩溃,全是系统的操作。它想不崩,那可以返回值代表处理错误或干脆没反应。它想崩那就崩。


总结一下空值区别

Nil:对类进行赋空值
nil:对对象进行赋空值
Null:对C指针进行赋空操作,如字符串数组的首地址 char *name = NULL
NSNull:对组合值,如NSArray,Json而言,其内部有值,但值为空


PS: 数组一边遍历,一边操作是会崩的。

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,170评论 0 7
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 744评论 0 1
  • Runtime是一套比较底层的纯C语言API,包含了很多底层的C语言API。在我们平时编写的OC代码中,程序运行时...
    这个年纪的情愫丶阅读 584评论 5 3
  • 活着就好--暮霭沉沉 长大以后,你说你想要人生的意义现实之中,大多数人说你是个垃圾你迷茫,你失望,再由失望变绝望想...
    不置一语阅读 127评论 0 0