对runtime的总结:让你会用Runtime
<h2>导语</h2> <p>Runtime,简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制,是一套底层的纯C语言的API,我们平时所编写的OC代码,在程序的运行过程中都转成了runtime的代码,平时调方法都是转成了objc_msgSend函数。大家应该都听过或者用过一个叼库IQKeyboard,它只需导入进工程,不需要写一行代码就能实现功能,其实就是用的runtime机制实现的,俗称黑魔法。</p> <h2>runtime的作用</h2> <p>Objective-C 的 Runtime 铸就了它动态语言的特性,这些深层次的知识虽然平时写代码用的少一些,但是却是每个 Objc 程序员需要了解的。</p> <ul> <li>在程序运行过程中,动态的创建一个类(KVO实现原理)以及动态的为每个类添加或修改属性、方法</li> <li>遍历一个类中所有的成员变量以及方法(NSCoding的自动解档和归档、MJExtension的实现)</li> <li>交换两个方法的实现(Swizzle黑魔法)</li> <li>...</li> </ul> <h2>KVO实现原理</h2> <p>当你第一次观察某个对象时,runtime会创建一个新的类NSKVONOtifying_class,该类继承自原先的class。在这个新的派生类中,它重写了所有被观察属性的setter方法,然后将对象的isa指针指向新创建的NSKVONOtifying_class(这个指针告诉Objective-C的runtime某个对象到底是哪种类型的对象)。所以该对象神奇地变成了新的子类的实例。当对象的属性发生改变时,会触发setter方法,但这个方法已经被重写了,并且在方法的内部添加了发送通知机制,实现了自动触发通知机制,这就是KVO实现的原理。</p> <p>交换两个方法的实现</p> <p>先看下面例子:</p> <p>创建一个Student类,实现两个类方法:studyJava和studyC</p> <p>使用runtime交换两个方法的实现:</p> <pre> <code class="language-objectivec">#import<objc/runtime.h>//别忘加运行时头文件 //获取class中的类方法 Method studyJava = class_getClassMethod([Student class], @selector(studyJava)); Method studyC = class_getClassMethod([Student class], @selector(studyC)); //交换方法的实现 method_exchangeImplementations(studyJava, studyC); [Student studyC]; [Student studyJava];</code></pre> <p>运行后会发现:让他先学C再学Java就是不听,结果是先学Java。</p> <p>看到这你会觉得并没有什么卵用,是的,这样写意义不大,但我们换个角度想,如果交换的是系统的方法呢?假如我们要统一监听控制器何时被销毁,用runtime可以让我们只写一次代码,看例子:</p> <p>首先新建一个UIViewController的分类,然后在里面写上代码:</p> <pre> <code class="language-objectivec">#import "UIViewController+Extension.h" #import <objc/runtime.h> @implementation UIViewController (Extension) //将交换方法写在load方法中,使得在该类被加载如内存中就调用该方法以交换方法 + (void)load { //class_getInstanceMethod :获取实例方法 Method dealloc = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc")); Method jyh_dealloc = class_getInstanceMethod(self, @selector(jyh_dealloc)); method_exchangeImplementations(dealloc, jyh_dealloc); } //自己实现的dealloc方法,监听销毁 - (void)jyh_dealloc { NSLog(@"%@ is dealloc", self); [self jyh_dealloc];//调用原来的dealloc方法 } @end</code></pre> <p>这样便实现了所用控制器销毁的监听。以上的例子只是为了说明runtime的作用,它可以有很多扩展,比如:将[UIImage imageNamed:]换成自己的方法,在方法中加入对图片名称的操作,就可以实现换肤或适配等功能;可以替换viewDidLoad方法实现用户进入页面的统计,不用每个控制器都复制粘贴同样的代码(或许你会想我们可以使用 OOP 的特性之一,继承的方式来解决这个问题。创建一个基类,在这个基类中添加统计方法,其他类都继承自这个基类。然而,这种方式修改还是很大,而且定制性很差。以后有新人加入之后,都要嘱咐其继承自这个基类,所以这种方式并不可取。)IQKeyboard库不需要写一行代码就能实现功能,就是通过这一原理实现的。</p> <h2>遍历类中的所有成员变量</h2> <p>有时候我们要对一些信息进行归档,如用户信息类UserInfo,这将需要重写initWithCoder和encodeWithCoder方法,并对每个属性进行encode和decode操作。那么问题来了:当属性只有几个的时候可以轻松写完,如果有几十个属性呢?我们可以 用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。</p> <pre> <code class="language-objectivec">- (instancetype)initWithCoder:(NSCoder *)aDecoder { if(self = [super init]) { unsigned int outCount; //获得所传入类的成员变量 Ivar *ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i++) { Ivar ivar = ivars[i]; NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)]; [self setValue:[aDecoder decodeObjectForKey:key] forKey:key]; } free(ivars);//别忘记释放 } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { unsigned int outCount; Ivar *ivars = class_copyIvarList([self class], &outCount); for (int i = 0; i < outCount; i++) { Ivar ivar = ivars[i]; NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)]; [aCoder encodeObject:[self valueForKey:key] forKey:key]; } free(ivars); }</code></pre> <p>同理,字典转模型用的也是这种方法。</p> <h2>访问苹果私有属性</h2> <p>利用runtime获取属性名</p> <pre> <code class="language-objectivec">objc_property_t *properties = class_copyPropertyList([UITextField class], &count);</code></pre> <p>这样就可以获取到UITextField中的所有属性,包括在头文件中苹果不想让你看到的。</p> <p>这有什么用呢?假设我们有一个需求,想改变输入框中占位文字的颜色,用runtime的这个作用可以让我们很方便的实现:</p> <p>先将输入框所包含的属性打印出来:</p> <pre> <code class="language-objectivec">+ (void)getProperties { unsigned int count = 0; objc_property_t *properties = class_copyPropertyList([UITextField class], &count); for (int i = 0; i<count; i++) { // 取出属性 objc_property_t property = properties[i]; // 打印属性名字 JYHLog(@"%s <----> %s", property_getName(property), property_getAttributes(property)); } free(properties); }</code></pre> <p>我们可以看到这样一个属性:</p> <p><img src="https://simg.open-open.com/show/b12c3b6895173d0386e9ac119463f415.png"></p> <h2>查看私有属性</h2> <p>顾名思义,这就是占位的Label,我们只要利用KVC就可以将其改成我们想要的样子:</p> <pre> <code class="language-objectivec">[self setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];</code></pre> <p>以上只是举一些例子来窥探runtime的一些作用,可以有很多扩展,还需我们继续去学习。</p> <p>如果文章对你有用的话请点下喜欢或关注,Thank You!</p> <p> </p> <p>来自:http://www.jianshu.com/p/2e63a4d9b7ca</p> <p> </p>
本文由用户 Mahalia25R 自行上传分享,仅供网友学习交流。所有权归原作者,若您的权利被侵害,请联系管理员。
转载本站原创文章,请注明出处,并保留原始链接、图片水印。
本站是一个以用户分享为主的开源技术平台,欢迎各类分享!