runtime源码下载地址
http://opensource.apple.com/tarballs/objc4/
运行时可以干嘛
参考文档
http://www.ianisme.com/ios/2019.html?utm_source=tuicool&utm_medium=referral
很清晰
代码参考
https://github.com/ianisme/IANRuntimeStudy
runtime的作用
比较基础的一个动态特性是通过String来生成Classes和Selectors。Cocoa提供了NSClassFromString
和NSSelectorFromString
方法,使用起来很简单:
Class stringclass = NSClassFromString(@"NSString");
于是我们就得到了一个string class。接下来:
NSString *myString = [stringclass stringWithString:@"Hello World"];
为什么要这么做呢?直接使用Class不是更方便?通常情况下是,但有些场景下这个方法会很有用。首先,可以得知是否存在某个class,NSClassFromString
会返回nil,如果运行时不存在该class的话。
另一个使用场景是根据不同的输入返回不同的class或method。比如你在解析一些数据,每个数据项都有要解析的字符串以及自身的类型(String,Number,Array)。你可以在一个方法里搞定这些,也可以使用多个方法。其中一个方法是获取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。
Method Swizzling
#import <objc/runtime.h>
@interface 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
isa swizzling
https://www.mikeash.com/pyblog/friday-qa-2009-01-23.html
KVO应用
object_setClass(myObject, [MySubclass class]);
这个设计特别方便 动态更改isa
消息转发
如果 resolve method 返回NO,运行时就进入下一步骤:
消息转发。有两种常见用例。
将消息转发到另一个可以处理该消息的object。
将多个消息转发到同一个方法。
消息转发分两步。
首先,运行时调用-forwardingTargetForSelector:,如果只是想把消息发送到另一个object,那么就使用这个方法,因为更高效。
如果想要修改消息,那么就要使用-forwardInvocation:,运行时将消息打包成NSInvocation,然后返回给你处理。处理完之后,调用invokeWithTarget:。
Cocoa有几处地方用到了消息转发,
主要的两个地方是代理(Proxies)和响应链(Responder Chain)。
NSProxy是一个轻量级的class,它的作用就是转发消息到另一个object。
如果想要惰性加载object的某个属性会很有用。NSUndoManager也有用到,不过是截取消息,之后再执行,而不是转发到其他的地方。
响应链是关于Cocoa如何处理与发送事件与行为到对应的对象。
比如说,使用Cmd+C执行了copy命令,会发送-copy:到响应链。
首先是First Responder,通常是当前的UI。
如果没有处理该消息,则转发到下一个-nextResponder。
这么一直下去直到找到能够处理该消息的object,或者没有找到,报错。