ios运行时那些事

前言

什么是运行时(runtime)?

首先我们要先知道编程语言有静态和动态之分。所谓静态语言,就是在程序运行前决定了所有的类型判断,类的所有成员、方法在编译阶段就确定好了内存地址。也就意味着所有类对象只能访问属于自己的成员变量和方法,否则编译器直接报错。比较常见的静态的语言如:java,c++,c等等。

而动态语言,恰恰相反,类型的判断、类的成员变量、方法的内存地址都是在程序的运行阶段才最终确定,并且还能动态的添加成员变量和方法。也就意味着你调用一个不存在的方法时,编译也能通过,甚至一个对象它是什么类型并不是表面我们所看到的那样,只有运行之后才能决定其真正的类型。相比于静态语言,动态语言具有较高的灵活性和可订阅性。而oc,正是一门动态语言。

介绍到这里,我想可以解释一下运行时是什么了?所谓运行时,就是程序在运行时做的一些事。苹果提供了一套纯c语言的api,即runtime。在iOS开发中runtime的特性使得oc这门语言具有独特的魅力,我们可以利用运行时处理一些特殊的事情,甚至你可以轻松的玩出一些逼格很高的花样来。下面就开始一起进入运行时的世界吧。

在正式进入篇幅之前,首先声明一下,本编的主旨是简要阐述运行时的一些机制和原理,重点是讲述运行时的一些常用用法,不会去深入探究底层的C语言api。

要了解运行时,我们得先了解oc的消息机制

那么什么是消息机制?

在Objective-C中,任何方法的调用,本质是发送消息。比如我们下面方法:

[obj  method];

编译器会自动转化为:

 objc_msgSend(obj, @selector (method));

也就是说我们在oc中调用任何一个方法,其实质是转换为runtime中的一个函数objc_msgSend(),这个函数的作用是向obj对象(方法的调用者)发送了一条消息,告诉它你该去执行某个方法。

所以,我们其实也可以直接用运行时去调用你想要调用的任何一个可调用的方法:

如:

Dog  *dog = [Dog alloc] init];  

[dog run:100];

等价于:

Dog  *dog  = objc_msgSend(objc_getClass("Dog"), @selector(alloc));

dog = objc_msgSend(dog, sel_registerName("init"));

 objc_msgSend(dog, sel_registerName("run:"),100); //调用带参数的方法

注:使用objc_msgSend()函数,须要先import <objc/message.h>


讲到这里,我们就可以说一说什么是oc消息机制,也就是一个方法的调用流程。

1、编译器会先将代码[obj  method]转化为objc_msgSend(obj, @selector (method))函数去执行。

2、在objc_msgSend()函数中,首先通过obj的isa指针找到(对象)obj对应的(类)class。

3、在class中会先去cache中 通过SEL查找对应函数method(cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去class中的消息列表methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

补充:

>在oc中,每一个对象都有一个isa指针变量,这个指针指向的是对象的类,我们可以通过isa指针访问一个对象的类

>方法都保存在类的消息列表中,这个列表其实是一个字典,key是selector,value是IMP(imp是一个指针类型,指向方法的实现),并且selector和IMP之间的关系是在运行时才决定的,而不是编译时。如此们就可以做出一些特别事情来。

我们可以用运行时做什么


1、互换方法的实现

上面说到selector和IMP之间的关系是在运行时才决定的,那我们是不是可以改变selector和IMP的对应关系呢?runtime就给我们提供了这么一个函数:

    void method_exchangeImplementations(Method m1, Method m2)

我们可以通过此函数交换两个方法的实现,在开发中,可能我们会经常遇到一种场景,想为系统的某个方法增加一些特定的功能,又不想改变原有的东西,想要做到无缝衔接,用runtime方法互换无疑是最完美的。下面以交换系统的dealloc方法的实现为例:

首先建一个NSObject类目NSObject+ExchangeMethod,在类目中为NSObject类扩展一个my_dealloc方法用于替换系统的dealloc方法,其.m文件实现如下:



这样,当一个类的dealloc方法被调用时,会执行my_dealloc方法里的实现,完全无需再对原有的代码做任何改动


2、动态添加方法

前面有说到,动态语言调用一个没有的方法时,编译阶段也不不会报错。比如:

Dog *dog = [Dog alloc] init];

[dog performSelector:@selector(eat)]];

注:dog类中没有声明也没实现eat方法

上面代码,编译阶段肯定会通过,但程序一运行时便直接抛出异常闪退,抛出异常的打印闭着眼睛也知道是 :-[Dog eat]: unrecognized selector sent to instance 0x7fac91d0eba0'。这也印证了动态语言的方法需要在运行阶段才最终确定。

从而,我们可以动态的为某个类添加方法,而苹果performSelector:这个方法也很好的为我们逃过编译报错提供了支持。示例代码如下:

届时,我们在如上面调用[dog performSelector:@selector(eat)]]时,就会去执行test函数了。


3、动态添加属性

这也是runtime的一个重量级功能了,我们经常会想为系统的类或者一些不便修改的第三方框架的类增加一些自定义的属性以满足开发的需求。这个时候我们还是首先会想到类目,但是问题来了,类目只能为一个类添加方法,不能添加属性。

怎么做呢,还是用到运行时,为类动态添加属性。示例代码:为NSobject类添加一个字符串类型的属性: NSString *name

首先我们还是为NSobject建一个类目,其.h文件如下:

这里我们用property,类目中用 property会自动生成set/get的声明,但是没有实现,也无法生成下划线的成员变量,我们需要手动实现set、get方法。其.m文件如下:


4、获取类中所有的成员变量和属性

在开发中,你可能会遇到想要改变系统自带的类的某一个值,却找不与之对应的api,然后你就在那找瞎了眼,找呀找,始终找不到。这个时候我们可以确定一点,苹果系统自带的类有很多私有的属性或成员变量没有公开出来,也就意味着苹果它不想让我们访问。我靠,那还搞毛,有时需求来了,我还非要访问不可,那怎么办?

用运行时获取类的所有成员变量,即便私有的也能获取的到,用的函数如下:

     Ivar *class_copyIvarList(Class cls, unsigned int *outCount)//获取类中所有的成员变量

     objc_property_t class_getProperty(Class cls, const char *name)//获取类中所有的属性

代码示例如下:

使用运行时获取类中所有成员变量,还是相当有用的,比如现在一些字典转模型框架,它需要获取到模型的所有属性名,以这个属性名为key,取到字典中对应的value,然后通过kvc给这个属性赋设置值。再比如,你要设置系统UITextField控件placeholder的颜色,你会发现你翻遍api也找不到一个属性和方法来设置,这时你用运行时获取UITextField类所有成员变量,你会发现有一个_placeholderLabel成员变量,我们只需:

[self.textField setValue:[UIColor blueColor] forKeyPath:@"_placeholderLabel.textColor"];


总结

以上便是运行时的一些常用用法,本文仅抛砖引玉,在ios开发的道路上,想要深入了解oc这门语言,runtime是一餐不容错过的盛宴。

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 742评论 0 1
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,531评论 33 466
  • 今天第四天了,今天课程讲了三位一体的标签话管理,从时间、空间、环境、特征来做标识、以及三种苹果的运用! 这次被选中...
    芳沁雅阅读 234评论 0 0
  • 自我提升阅读学习第153天(9月17日) 紧张的三天培训终于拿到了冠军,带着激动地心情,我们登上了回家的高...
    vv167阅读 401评论 0 1