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的作用
比較基礎的一個動態(tài)特性是通過String來生成Classes和Selectors郑叠。Cocoa提供了NSClassFromString
和NSSelectorFromString
方法,使用起來很簡單:
Class stringclass = NSClassFromString(@"NSString");
于是我們就得到了一個string class亩鬼。接下來:
NSString *myString = [stringclass stringWithString:@"Hello World"];
為什么要這么做呢被因?直接使用Class不是更方便拢蛋?通常情況下是玄妈,但有些場景下這個方法會很有用凰荚。首先,可以得知是否存在某個class叽讳,NSClassFromString
會返回nil追他,如果運行時不存在該class的話。
另一個使用場景是根據(jù)不同的輸入返回不同的class或method绽榛。比如你在解析一些數(shù)據(jù)湿酸,每個數(shù)據(jù)項都有要解析的字符串以及自身的類型(String婿屹,Number灭美,Array)。你可以在一個方法里搞定這些昂利,也可以使用多個方法届腐。其中一個方法是獲取type,然后使用if來調(diào)用匹配的方法蜂奸。另一種是根據(jù)type來生成一個selector犁苏,然后調(diào)用之。以下是兩種實現(xiàn)方式:
- (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行围详。將來如果有新的類型,只需增加實現(xiàn)方法即可祖屏,而不用再去添加新的 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]);
這個設計特別方便 動態(tài)更改isa
消息轉(zhuǎn)發(fā)
如果 resolve method 返回NO,運行時就進入下一步驟:
消息轉(zhuǎn)發(fā)袁勺。有兩種常見用例雹食。
將消息轉(zhuǎn)發(fā)到另一個可以處理該消息的object。
將多個消息轉(zhuǎn)發(fā)到同一個方法期丰。
消息轉(zhuǎn)發(fā)分兩步群叶。
首先吃挑,運行時調(diào)用-forwardingTargetForSelector:,如果只是想把消息發(fā)送到另一個object街立,那么就使用這個方法舶衬,因為更高效。
如果想要修改消息赎离,那么就要使用-forwardInvocation:约炎,運行時將消息打包成NSInvocation,然后返回給你處理蟹瘾。處理完之后圾浅,調(diào)用invokeWithTarget:。
Cocoa有幾處地方用到了消息轉(zhuǎn)發(fā)憾朴,
主要的兩個地方是代理(Proxies)和響應鏈(Responder Chain)狸捕。
NSProxy是一個輕量級的class,它的作用就是轉(zhuǎn)發(fā)消息到另一個object众雷。
如果想要惰性加載object的某個屬性會很有用灸拍。NSUndoManager也有用到,不過是截取消息砾省,之后再執(zhí)行鸡岗,而不是轉(zhuǎn)發(fā)到其他的地方。
響應鏈是關于Cocoa如何處理與發(fā)送事件與行為到對應的對象编兄。
比如說轩性,使用Cmd+C執(zhí)行了copy命令,會發(fā)送-copy:到響應鏈狠鸳。
首先是First Responder揣苏,通常是當前的UI。
如果沒有處理該消息件舵,則轉(zhuǎn)發(fā)到下一個-nextResponder卸察。
這么一直下去直到找到能夠處理該消息的object,或者沒有找到铅祸,報錯坑质。