济南网站建设小程序开发,网页网站制作培训班,计算机多媒体毕业设计网站建设,求和萝莉做的网站一、什么是运行时#xff08;Runtime#xff09;? 运行时是苹果提供的纯C语言的开发库#xff08;运行时是一种非常牛逼、开发中经常用到的底层技术#xff09;二、运行时的作用#xff1f; 能获得某个类的所有成员变量能获得某个类的所有属性能获得某个类的所有方法交换…一、什么是运行时Runtime? 运行时是苹果提供的纯C语言的开发库运行时是一种非常牛逼、开发中经常用到的底层技术二、运行时的作用 能获得某个类的所有成员变量能获得某个类的所有属性能获得某个类的所有方法交换方法实现能动态添加一个成员变量能动态添加一个属性能动态添加一个方法三、案例运行时获取成员变量名称 1、分析#import Foundation/Foundation.h
#import XMGPerson.h #import objc/runtime.h int main(int argc, const char * argv[]) { autoreleasepool { // 成员变量的数量 unsigned int outCount  0; // 获得所有的成员变量 // ivars是一个指向成员变量的指针 // ivars默认指向第0个成员变量最前面 Ivar *ivars  class_copyIvarList([XMGPerson class], outCount); // 遍历所有的成员变量 for (int i  0; ioutCount; i) { // 取出i位置对应的成员变量 // Ivar ivar  *(ivars  i); Ivar ivar  ivars[i]; // 获得成员变量的名字 NSLog(%s, ivar_getName(ivar)); } // 如果函数名中包含了copy\new\retain\create等字眼那么这个函数返回的数据就需要手动释放 free(ivars); // Ivar ivar  *ivars; // Ivar ivar2  *(ivars  1); // NSLog(%s %s, ivar_getName(ivar), ivar_getName(ivar2)); // 一个Ivar就代表一个成员变量 // int *p; 指向int类型的变量 // Ivar *ivars; 指向Ivar类型的变量 } return 0; } 2、获取UITextFiled成员变量的名称Snip20151027_1.png   // 成员变量的数量unsigned int outCount  0;// 获得所有的成员变量 Ivar *ivars  class_copyIvarList([UITextField class], outCount); // 遍历所有的成员变量 for (int i  0; ioutCount; i) { // 取出i位置对应的成员变量 Ivar ivar  ivars[i]; // 获得成员变量的名字 NSLog(%s, ivar_getName(ivar)); } // 如果函数名中包含了copy\new\retain\create等字眼那么这个函数返回的数据就需要手动释放 free(ivars); 四、iOS底层 1、The Runtime 简单介绍 Objective-C是一门简单的语言95%是C。只是在语言层面上加了些关键字和语法。真正让Objective-C如此强大的是它的运行时。它很小但却很强大。它的核心是消息分发。Messages 执行一个方法有些语言编译器会执行一些额外的优化和错误检查因为调用关系很直接也很明显。但对于消息分发来说就不那么明显了。在发消息前不必知道某个对象是否能够处理消息。你把消息发给它它可能会处理也可能转给其他的Object来处理。一个消息不必对应一个方法一个对象可能实现一个方法来处理多条消息。在Objective-C中消息是通过objc_msgSend()这个runtime方法及相近的方法来实现的。这个方法需要一个targetselector还有一些参数。理论上来说编译器只是把消息分发变成objc_msgSend来执行。比如下面这两行代码是等价的。[array insertObject:foo atIndex:5];
objc_msgSend(array, selector(insertObject:atIndex:), foo, 5); Objects, Classes, MetaClasses 大多数面向对象的语言里有 classes 和 objects 的概念。Objects通过Classes生成。但是在Objective-C中classes本身也是objects也可以处理消息这也是为什么会有类方法和实例方法。具体来说Objective-C中的Object是一个结构体(struct)第一个成员是isa指向自己的class。这是在objc/objc.h中定义的。typedef struct objc_object {Class isa;
} *id; object的class保存了方法列表还有指向父类的指针。但classes也是objects也会有isa变量那么它又指向哪儿呢这里就引出了第三个类型: metaclasses。一个 metaclass被指向classclass被指向object。它保存了所有实现的方法列表以及父类的metaclass。如果想更清楚地了解objects,classes以及metaclasses是如何一起工作地可以阅读这篇文章。Methods, Selectors and IMPs  我们知道了运行时会发消息给对象。我们也知道一个对象的class保存了方法列表。那么这些消息是如何映射到方法的这些方法又是如何被执行的呢  第一个问题的答案很简单。class的方法列表其实是一个字典key为selectorsIMPs为value。一个IMP是指向方法在内存中的实现。很重要的一点是selector和IMP之间的关系是在运行时才决定的而不是编译时。这样我们就能玩出些花样。  IMP通常是指向方法的指针第一个参数是self类型为id第二个参数是_cmd类型为SEL余下的是方法的参数。这也是self和_cmd被定义的地方。下面演示了Method和IMP - (id)doSomethingWithInt:(int)aInt{}id doSomethingWithInt(id self, SEL _cmd, int aInt){} 现在我们知道了objects,classes,selectors,IMPs以及消息分发那么运行时到底能做什么呢运行时到底能做什么呢  作用 创建、修改、自省classes和objects消息分发 之前已经提过消息分发不过这只是一小部分功能。所有的运行时方法都有特定的前缀。下面是一些有意思的方法 class class开头的方法是用来修改和自省classes。方法如 能拿到一个class的所有内容 class_addIvar, class_addMethod, class_addProperty和class_addProtocol允许重建classes。class_copyIvarList, class_copyMethodList, class_copyProtocolList和class_copyPropertyList 返回单个内容 class_getClassMethod, class_getClassVariable, class_getInstanceMethod, class_getInstanceVariable, class_getMethodImplementation和class_getProperty 一些通用的自省方法 class_conformsToProtocol, class_respondsToSelector, class_getSuperclass 创建一个object class_createInstance来创建一个object ivar 这些方法能让你得到名字内存地址和Objective-C type encoding。method 这些方法主要用来自省比如:method_getName, method_getImplementation, method_getReturnType等等 也有一些修改的方法包括: method_setImplementation和method_exchangeImplementations objc 一旦拿到了object你就可以对它做一些自省和修改。你可以get/set ivar, 使用object_copy和object_dispose来copy和free object的内存。不仅是拿到一个class而是可以使用object_setClass来改变一个object的class。property 属性保存了很大一部分信息。除了拿到名字你还可以使用property_getAttributes来发现property的更多信息如返回值、是否为atomic、getter/setter名字、是否为dynamic、背后使用的ivar名字、是否为弱引用。protocol Protocols有点像classes但是精简版的运行时的方法是一样的。你可以获取method, property, protocol列表, 检查是否实现了其他的protocol。sel 最后我们有一些方法可以处理 selectors比如获取名字注册一个selector等等。2、运行时能干什么举例 2.1 Classes And Selectors From Strings  比较基础的一个动态特性是通过String来生成Classes和Selectors。Cocoa提供了NSClassFromString和NSSelectorFromString方法使用起来很简单 Class stringclass  NSClassFromString(NSString)  于是我们就得到了一个string class。接下来 NSString *myString  [stringclass stringWithString:Hello World];  为什么要这么做呢直接使用Class不是更方便通常情况下是但有些场景下这个方法会很有用。首先可以得知是否存在某个classNSClassFromString 会返回nil如果运行时不存在该class的话。  另一个使用场景是根据不同的输入返回不同的class或method。比如你在解析一些数据每个数据项都有要解析的字符串以及自身的类型StringNumberArray。你可以在一个方法里搞定这些也可以使用多个方法。其中一个方法是获取type然后使用if来调用匹配的方法。另一种是根据type来生成一个selector然后调用之。以下是两种实现方式 - (void)parseObject:(id)object {for (id data in object) {if ([[data type] isEqualToString:String]) { [self parseString:[data value]]; } else if ([[data type] isEqualToString:Number]) { [self parseNumber:[data value]]; } else if ([[data type] isEqualToString:Array]) { [self parseArray:[data value]]; } } } - (void)parseObjectDynamic:(id)object { for (id data in object) { [self performSelector:NSSelectorFromString([NSString stringWithFormat:parse%:, [data type]]) withObject:[data value]]; } } - (void)parseString:(NSString *)aString {} - (void)parseNumber:(NSString *)aNumber {} - (void)parseArray:(NSString *)aArray {} 可一看到你可以把7行带if的代码变成1行。将来如果有新的类型只需增加实现方法即可而不用再去添加新的 else if。2.2 Method Swizzling  之前我们讲过方法由两个部分组成。Selector相当于一个方法的idIMP是方法的实现。这样分开的一个便利之处是selector和IMP之间的对应关系可以被改变。比如一个 IMP 可以有多个 selectors 指向它。  而 Method Swizzling 可以交换两个方法的实现。或许你会问“什么情况下会需要这个呢”。我们先来看下Objective-C中两种扩展class的途径。首先是 subclassing。你可以重写某个方法调用父类的实现这也意味着你必须使用这个subclass的实例但如果继承了某个Cocoa class而Cocoa又返回了原先的class(比如 NSArray)。这种情况下你会想添加一个方法到NSArray也就是使用Category。99%的情况下这是OK的但如果你重写了某个方法就没有机会再调用原先的实现了。  Method Swizzling 可以搞定这个问题。你可以重写某个方法而不用继承同时还可以调用原先的实现。通常的做法是在category中添加一个方法(当然也可以是一个全新的class)。可以通过method_exchangeImplementations这个运行时方法来交换实现。来看一个demo这个demo演示了如何重写addObject:方法来纪录每一个新添加的对象。 #import  objc/runtime.hinterface NSMutableArray (LoggingAddObject) - (void)logAddObject:(id)aObject; end implementation NSMutableArray (LoggingAddObject)  (void)load { Method addobject  class_getInstanceMethod(self, selector(addObject:)); Method logAddobject  class_getInstanceMethod(self, selector(logAddObject:)); method_exchangeImplementations(addObject, logAddObject); } - (void)logAddObject:(id)aobject { [self logAddObject:aObject]; NSLog(Added object % to array %, aObject, self); } end 我们把方法交换放到了load中这个方法只会被调用一次而且是运行时载入。如果指向临时用一下可以放到别的地方。注意到一个很明显的递归调用logAddObject:。这也是Method Swizzling容易把我们搞混的地方因为我们已经交换了方法的实现所以其实调用的是addObject:动态继承、交换  我们可以在运行时创建新的class这个特性用得不多但其实它还是很强大的。你能通过它创建新的子类并添加新的方法。  但这样的一个子类有什么用呢别忘了Objective-C的一个关键点object内部有一个叫做isa的变量指向它的class。这个变量可以被改变而不需要重新创建。然后就可以添加新的ivar和方法了。可以通过以下命令来修改一个object的class. object_setClass(myObject, [MySubclass class]);  这可以用在Key Value Observing。当你开始observing an object时Cocoa会创建这个object的class的subclass然后将这个object的isa指向新创建的subclass。 动态方法处理  目前为止我们讨论了方法交换以及已有方法的处理。那么当你发送了一个object无法处理的消息时会发生什么呢很明显it breaks。大多数情况下确实如此但Cocoa和runtime也提供了一些应对方法。  首先是动态方法处理。通常来说处理一个方法运行时寻找匹配的selector然后执行之。有时你只想在运行时才创建某个方法比如有些信息只有在运行时才能得到。要实现这个效果你需要重写resolveInstanceMethod: 和/或 resolveClassMethod:。如果确实增加了一个方法记得返回YES。  (BOOL)resolveInstanceMethod:(SEL)aSelector {if (aSelector  selector(myDynamicMethod)) {class_addMethod(self, aSelector, (IMP)myDynamicIMP, v:);return YES;}return [super resolveInstanceMethod:aSelector]; } 那Cocoa在什么场景下会使用这些方法呢Core Data用得很多。NSManagedObjects有许多在运行时添加的属性用来处理get/set属性和关系。那如果Model在运行时被改变了呢消息转发  如果 resolve method 返回NO运行时就进入下一步骤消息转发。有两种常见用例。1) 将消息转发到另一个可以处理该消息的object。2) 将多个消息转发到同一个方法。  消息转发分两步。首先运行时调用-forwardingTargetForSelector:如果只是想把消息发送到另一个object那么就使用这个方法因为更高效。如果想要修改消息那么就要使用-forwardInvocation:运行时将消息打包成NSInvocation然后返回给你处理。处理完之后调用invokeWithTarget:。  Cocoa有几处地方用到了消息转发主要的两个地方是代理(Proxies)和响应链(Responder Chain)。NSProxy是一个轻量级的class它的作用就是转发消息到另一个object。如果想要惰性加载object的某个属性会很有用。NSUndoManager也有用到不过是截取消息之后再执行而不是转发到其他的地方。  响应链是关于Cocoa如何处理与发送事件与行为到对应的对象。比如说使用CmdC执行了copy命令会发送-copy:到响应链。首先是First Responder通常是当前的UI。如果没有处理该消息则转发到下一个-nextResponder。这么一直下去直到找到能够处理该消息的object或者没有找到报错。 使用Block作为Method IMP iOS 4.3带来了很多新的runtime方法。除了对properties和protocols的加强还带来一组新的以 imp 开头的方法。通常一个 IMP 是一个指向方法实现的指针头两个参数为 object(self)和selector(_cmd)。iOS 4.0和Mac OS X 10.6 带来了blockimp_implementationWithBlock() 能让我们使用block作为 IMP下面这个代码片段展示了如何使用block来添加新的方法。IMP myIMP  imp_implementationWithBlock(^(id _self, NSString *string) {NSLog(Hello %, string); }); class_addMethod([MYclass class], selector(sayHello:), myIMP, v:); 可以看到Objective-C 表面看起来挺简单但还是很灵活的可以带来很多可能性。动态语言的优势在于在不扩展语言本身的情况下做很多很灵巧的事情。比如Key Value Observing提供了优雅的API可以与已有的代码无缝结合而不需要新增语言级别的特性。转载于:https://www.cnblogs.com/liuxiaokun/p/6505612.html