iOS重要知识-- KVC、KVO、runloop、runtime

KVC

相信即使没用过,你也大概听到过。KeyValueCoding。用自己话简单概括下,就是通过实例变量或者成员变量的属性名称,来访问其属性或者说使用它们的getter、setter方法。具体如下:

假如我们有个叫做Student的类,它有个成员变量叫做age,还有个叫做name。两个属性叫做age1、name1。我们知道,用@property修饰过的属性们,编译器给我们生成了getter、setter方法。调用的时候,我们只需要用点语法就能调用。但成员变量,如果没有重写他们setter、getter方法,我们怎么办。

student*stu = [[student alloc]init];

NSString*name = [stu valueForKey:@"name"];

 [stu setValue:@"axiba"forKey:@"name"];

如上,首先是实例化一个变量stu,然后使用valueForKey获取该对象的name属性,赋值给name变量。然后setValue Forkey方法对stu的name属性进行赋值。注意,这里最关键的参数是key值,千万别写错。

好了KVC讲完,我们继续讲KVO。

KVO

KeyValueObserving。换句话说,这叫键值对监测,利用实例变量观察某实例属性值的变化(这里的观察者和被观察者不一定是同一个,也就是说,可以用一个对象观测另一个对象的某个属性变化,来达到对观察者进行操作的目的)。KVO 的实现也依赖于 Objective-C 强大的 Runtime 。KVO 是 Objective-C 对观察者模式(Observer Pattern)的实现。也是 Cocoa Binding 的基础。当被观察对象的某个属性发生更改时,观察者对象会获得通知。使用步骤:

1.注册需要观察的对象的属性addObserver:forKeyPath:options:context:

//第一个参数observer:观察者对象

//第二个参数keyPath: 被观察的属性名称

//第三个参数options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)

//第四个参数context: 上下文,可以为kvo的回调方法传值(例如设定为一个放置数据的字典)

[被观察者 addObserver:观察者 forKeyPath:@"被观察者的属性名称"options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNewcontext:nil];

2.实现observeValueForKeyPath:ofObject:change:context:方法,这个方法当观察的属性变化时会自动调用

//keyPath:属性名称

//object:被观察的对象

//change:变化前后的值都存储在change字典中

//context:注册观察者时,context传过来的值-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)objectchange:(NSDictionary *)changecontext:(void*)context{

}

3.取消注册观察removeObserver:forKeyPath:context:

看起来挺像NSNotification的,需要注意的是千万别把keyPath写错,还有就是别调用两次的removeObserve会导致程序崩溃,你可能说,这么低级的错误我怎么可能会犯。当然写两句removeObserve的确不大可能,除非手滑。但如果你的父类和子类都写了removeObserve,就不易看不来了,这时候也会导致崩溃。

还有需要注意的是,addobserver方法,是被观察者添加了一个观察者,来检测自己某个属性的变化,这个被观察者可以使别的类的对象,也可以是本类的对象。


runloop

RunLoop顾名思义,就是运行循环的意思。Runloop是事件接收和分发机制的一个实现。Runloop提供了一种异步执行代码的机制,不能并行执行任务。在主队列中,Main RunLoop直接配合任务的执行,负责处理UI事件、定时器以及其他内核相关事件。

(1).RunLoop的主要目的:保持程序的持续运行、处理App中的各类事件(触摸事件、定时器事件、Selector事件)、节省CPU资源,提高程序性能,没有事件时就进入睡眠状态。

(2).什么时候使用Runloop :1.需要使用Port(基于端口)或者自定义(事件源)Input Source与其他线程进行通讯。2.需要在线程中使用Timer。3.需要在线程上使用performSelector*****方法。4.需要让线程执行周期性的工作。

主线程��默认有Runloop。当自己启动一个线程,如果只是用于处理单一的事件,则该线程在执行完之后就退出了。所以当我们需要让该线程监听某项事务时,就得让线程一直不退出,runloop就是这么一个循环,没有事件的时候,一直休眠,有事件来临了,执行其对应的函数。

这些就是runloop的基本重点,至于怎么实现,就不详述了,网上很多。runloop是runtime消息转发机制的具体实现。

runtime

RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。

runtime我们看一行代码,一张图。

[self goHome];//这是我们常见的方法调用,通过clang编译器,它却是这样的

objc_msgSend(obj,@selector(goHome));

首先,编译器将代码[obj goHome];转化为objc_msgSend(obj,@selector(GoHome));,在objc_msgSend函数中。首先通过obj的isa指针找到obj对应的class。在Class中先去cache中通过SEL查找对应函数method(猜测cache中method列表是以SEL为key通过hash表来存储的,这样能提高函数查找速度),若cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。

下面关于SEL方法选择器,着重去学习了下,查阅到如下资料。

SEL其主要作用是快速的通过方法名字(goHome)查找到对应方法的函数指针,然后调用其函数。SEL其本身是一个Int类型的一个地址,地址中存放着方法的名字。对于一个类中。每一个方法对应着一个SEL。所以iOS类中不能存在2个名称相同的方法,即使参数类型不同,因为SEL是根据方法名字生成的,相同的方法名称只能对应一个SEL。Objective-C在编译时,会根据方法的名字生成一个用来区分这个方法的唯一的一个ID。只要方法名称相同,那么它们的ID就是相同的。两个类之间,不管它们是父类与子类的关系,还是之间没有这种关系,只要方法名相同,那么它的SEL就是一样的。每一个方法都对应着一个SEL。编译器会根据每个方法的方法名为那个方法生成唯一的SEL。这些SEL组成了一个Set集合,当我们在这个集合中查找某个方法时,只需要去找这个方法对应的SEL即可。而SEL本质是一个字符串,所以直接比较它们的地址即可。

当然,不同的类可以拥有相同的selector。不同类的实例对象执行相同的selector时,会在各自的方法列表中去根据selector去寻找自己对应的IMP。

对了,还剩下一张图。

runtime消息转发机制

metaclass就是元类。指向metaclass,也就是静态的Class。一般一个Obj对象中的isa会指向普通的Class,这个Class中存储普通成员变量和对象方法(“-”开头的方法),普通Class中的isa指针指向静态Class,静态Class中存储static类型成员变量和类方法(“+”开头的方法)。Class所有metaclass中isa指针都指向跟metaclass。而跟metaclass则指向自身。Root metaclass是通过继承Root class产生的。与root class结构体成员一致,也就是前面提到的结构。不同的是Root metaclass的isa指针指向自身。

super_class:指向父类,如果这个类是根类,则为NULL。


总算搞完了,iOS开发者如果只要能干活,那么可能你压根不需要知道上面这些令人烦躁又枯燥的东西,但这些却是衡量一个iOS开发者进入中级阶段必须要知道的内容,不然面试官估计直接把你pass了……即使以后你被录用了,工作上用不到这些,但你还是得懂。所以一遍不懂就两遍,两遍不懂就三遍。虽然是笨办法,但又有什么关系,能成功就好了。蜗牛精神,一步一步往上爬!

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

推荐阅读更多精彩内容

  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    SOI阅读 21,774评论 3 63
  • Runloop 做了一年多的IOS开发,对IOS和Objective-C深层次的了解还十分有限,大多还停留在会用A...
    GitHubPorter阅读 6,040评论 7 17
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,200评论 0 7
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 文/完美的补丁 (以此文献给与我一样曾经“梦想”冲在时代的前端,“现实”却远远抛在多数人...
    完美的补丁阅读 775评论 9 6