Runtime
基本是用C和匯編寫(xiě)的
Runtime 涉及三個(gè)點(diǎn)罢缸,面向?qū)ο箐桃颍⒎职l(fā)呵俏,消息轉(zhuǎn)發(fā)
面向?qū)ο?/h4>
Objective-C 的對(duì)象是基于 Runtime 創(chuàng)建的結(jié)構(gòu)體
image
消息分發(fā)
// 創(chuàng)建person對(duì)象
Person *p = [[Person alloc] init];
// 調(diào)用對(duì)象方法
[p eat];
// 本質(zhì):讓對(duì)象發(fā)送消息
objc_msgSend(p, @selector(eat));
// 調(diào)用類方法的方式:兩種
// 第一種通過(guò)類名調(diào)用
[Person eat];
// 第二種通過(guò)類對(duì)象調(diào)用
[[Person class] eat];
// 用類名調(diào)用類方法畔师,底層會(huì)自動(dòng)把類名轉(zhuǎn)換成類對(duì)象調(diào)用
// 本質(zhì):讓類對(duì)象發(fā)送消息
objc_msgSend([Person class], @selector(eat));
消息轉(zhuǎn)發(fā)
Objective-C 的對(duì)象是基于 Runtime 創(chuàng)建的結(jié)構(gòu)體
// 創(chuàng)建person對(duì)象
Person *p = [[Person alloc] init];
// 調(diào)用對(duì)象方法
[p eat];
// 本質(zhì):讓對(duì)象發(fā)送消息
objc_msgSend(p, @selector(eat));
// 調(diào)用類方法的方式:兩種
// 第一種通過(guò)類名調(diào)用
[Person eat];
// 第二種通過(guò)類對(duì)象調(diào)用
[[Person class] eat];
// 用類名調(diào)用類方法畔师,底層會(huì)自動(dòng)把類名轉(zhuǎn)換成類對(duì)象調(diào)用
// 本質(zhì):讓類對(duì)象發(fā)送消息
objc_msgSend([Person class], @selector(eat));
完整的消息轉(zhuǎn)發(fā)流程圖:
- 1薄霜、動(dòng)態(tài)方法解析
- +(BOOL)resolveInstanceMethod:(SEL)sel;
當(dāng)接受到未能識(shí)別的SEL時(shí),運(yùn)行時(shí)系統(tǒng)會(huì)調(diào)用該函數(shù)用以給對(duì)象一次機(jī)會(huì)來(lái)添加相應(yīng)的方法實(shí)現(xiàn)漱凝,如果用戶在該函數(shù)中動(dòng)態(tài)添加了相應(yīng)方法的實(shí)現(xiàn)疮蹦,則跳轉(zhuǎn)到方法的實(shí)現(xiàn)部分,并將該實(shí)現(xiàn)存入緩存中碉哑,以供下次調(diào)用挚币。
- 2、備援接收者
- -(id)forwardingTargetForSelector:(SEL)aSelector;
如果運(yùn)行時(shí)在消息轉(zhuǎn)發(fā)的第一步中未找到所調(diào)用方法的實(shí)現(xiàn)扣典,那么當(dāng)前接收者還有第二次機(jī)會(huì)進(jìn)行未知SEL的處理妆毕。這時(shí)運(yùn)行期系統(tǒng)會(huì)調(diào)用上述方法,并將未知SEL作為參數(shù)傳入贮尖,該方法可以返回一個(gè)能處理該選擇子的對(duì)象笛粘,運(yùn)行時(shí)系統(tǒng)會(huì)根據(jù)返回的對(duì)象進(jìn)行查找,若找到則跳轉(zhuǎn)到相應(yīng)方法的實(shí)現(xiàn)湿硝,則消息轉(zhuǎn)發(fā)結(jié)束薪前。
- 3、完整的消息轉(zhuǎn)發(fā)
- -(void)forwardInvocation:(NSInvocation *)anInvocation;
當(dāng)運(yùn)行時(shí)系統(tǒng)檢測(cè)到第二步中用戶未返回能處理相應(yīng)選擇子的對(duì)象時(shí)关斜,那么來(lái)到這一步就要啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制了示括。該方法可以改變消息調(diào)用目標(biāo),運(yùn)行時(shí)系統(tǒng)根據(jù)所改變的調(diào)用目標(biāo)痢畜,向調(diào)用目標(biāo)方法列表中查詢對(duì)應(yīng)方法的實(shí)現(xiàn)并實(shí)現(xiàn)跳轉(zhuǎn)垛膝,這種方式和第二步的操作非常相似。當(dāng)然你也可以修改方法的選擇子丁稀,亦或者向所調(diào)用方法中追加一個(gè)參數(shù)等來(lái)跳轉(zhuǎn)到相關(guān)方法的實(shí)現(xiàn)吼拥。
最后,如果消息轉(zhuǎn)發(fā)的第三步還未能處理該未知選擇子的話线衫,那么最終會(huì)調(diào)用NSObject類的如下方法用以異常的拋出凿可,表明該選擇子最終未能處理。
- -(void)doesNotRecognizeSelector:(SEL)aSelector;
常用方式
替換系統(tǒng)方法
獲得某個(gè)類的類方法
Method class_getClassMethod(Class cls , SEL name)
獲得某個(gè)類的實(shí)例對(duì)象方法
Method class_getInstanceMethod(Class cls , SEL name)
交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations(Method m1 , Method m2)
攔截系統(tǒng)方法
1.建一個(gè)分類(UIImage+Category)
2.分類中實(shí)現(xiàn)一個(gè)自定義方法授账,方法中寫(xiě)要在系統(tǒng)方法中加入的語(yǔ)句
+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
// 如果系統(tǒng)版本是7.0以上枯跑,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片
name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}
3.分類中重寫(xiě)UIImage的load方法惨驶,實(shí)現(xiàn)方法的交換
+ (void)load {
// 獲取兩個(gè)類的類方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
// 開(kāi)始交換方法實(shí)現(xiàn)
method_exchangeImplementations(m1, m2);
}
實(shí)現(xiàn)分類增加屬性(關(guān)聯(lián)對(duì)象)
.h
@property(nonatomic,copy)NSString *name;
.m
static NSString * const nameKey = @"nameKey";
- (void)setName:(NSString *)name {
// 將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來(lái),將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中
objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, &nameKey);
}
自動(dòng)歸檔解檔
自定義對(duì)象不能用于writeToFile保存敛助,需要用于歸檔來(lái)進(jìn)行保存
NSObject+Extension
- (void)encodeWithCoder:(NSCoder *)encoder
{
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([Movie class], &count);
for (int i = 0; i<count; i++) {
// 取出i位置對(duì)應(yīng)的成員變量
Ivar ivar = ivars[i];
// 查看成員變量
const char *name = ivar_getName(ivar);
// 歸檔
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[encoder encodeObject:value forKey:key];
}
free(ivars);
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([Movie class], &count);
for (int i = 0; i<count; i++) {
// 取出i位置對(duì)應(yīng)的成員變量
Ivar ivar = ivars[i];
// 查看成員變量
const char *name = ivar_getName(ivar);
// 歸檔
NSString *key = [NSString stringWithUTF8String:name];
id value = [decoder decodeObjectForKey:key];
// 設(shè)置到成員變量身上
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
@end
字典敞咧、Json、Model轉(zhuǎn)換
//字典數(shù)據(jù)轉(zhuǎn)Model
User *user = [[User alloc] init];
//獲取當(dāng)前model所有屬性
unsigned int propertiesCount;
objc_property_t *properties = class_copyPropertyList([user class], &propertiesCount);
for (NSUInteger i = 0; i < propertiesCount; i++){
//獲取property Name
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
id value = [json valueForKey:@(propertyName)];
//使用KVC形式進(jìn)行對(duì)象賦值
[user performSelector:@selector(setValue:forKey:) withObject:value withObject:@(propertyName)];
}
上述代碼僅僅可以做到NSDictionary轉(zhuǎn)Model辜腺,并且Model不能包含自定義對(duì)象
完整代碼可能需要單獨(dú)來(lái)介紹---->Runtime 字典、Json乍恐、Model(含內(nèi)嵌對(duì)象)轉(zhuǎn)換
動(dòng)態(tài)增加方法
- 開(kāi)發(fā)使用場(chǎng)景:如果一個(gè)類方法非常多评疗,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源,需要給每個(gè)方法生成映射表茵烈,可以使用動(dòng)態(tài)給某個(gè)類百匆,添加方法解
- 經(jīng)典面試題:怎么調(diào)用類的私有方法?有沒(méi)有使用performSelector呜投,其實(shí)主要想問(wèn)你有沒(méi)有動(dòng)態(tài)添加過(guò)方法加匈。
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
//還可以調(diào)用私有方法
[p performSelector:@selector(run:) withObject:@10];
}
@end
#import "Person.h"
#import <objc/message.h>
@implementation Person
// 沒(méi)有返回值,也沒(méi)有參數(shù) --> void,(id,SEL)
void aaa(id self, SEL _cmd, NSNumber *meter) {
NSLog(@"跑了%@", meter);
}
// 任何方法默認(rèn)都有兩個(gè)隱式參數(shù),self,_cmd(_cmd代表方法編號(hào),打印結(jié)果為當(dāng)前執(zhí)行的方法名)
// 什么時(shí)候調(diào)用:只要一個(gè)對(duì)象調(diào)用了一個(gè)未實(shí)現(xiàn)的方法就會(huì)調(diào)用這個(gè)方法,進(jìn)行處理
// 作用:動(dòng)態(tài)添加方法,處理未實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"run:")) {
//aaa不會(huì)生成方法列表
class_addMethod(self, sel, (IMP)aaa, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
動(dòng)態(tài)變量控制
在程序中仑荐,Person的私有屬性age是10雕拼,使用runtime變成了20。
Person *p = [[Person alloc] init];
Ivar *ivar = class_copyIvarList([p class], &count);
const char *varName = ivar_getName(var);
object_setIvar(p, var, @"20");
實(shí)現(xiàn)萬(wàn)能調(diào)整(Router)
- (void)push:(NSString *)classFromString{
Class actionClass = NSClassFromString(classFromString);
id target = [[actionClass alloc] init];
if(!target){
NSLog(@"沒(méi)有當(dāng)前類");
}
if (![(NSObject *) target isKindOfClass:[UIViewController class]]) {
NSLog(@"當(dāng)前不是UIViewController");
}
UINavigationController *navigationCtr = [self getActiveNavigationController];
if ([navigationCtr isKindOfClass:[UINavigationController class]]) {
[navigationCtr pushViewController:(UIViewController *)target animated:YES];
}
}
- (UINavigationController *)getActiveNavigationController
{
if ([[UIApplication sharedApplication] delegate].window.isKeyWindow)
{
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([rootViewController isKindOfClass:[UITabBarController class]])
{
UINavigationController *navigationController = ((UITabBarController *) rootViewController).selectedViewController;
return navigationController;
} else if ([rootViewController isKindOfClass:[UINavigationController class]])
return (UINavigationController *) rootViewController;
else
return nil;
} else
return nil;
}
by 有涯sui無(wú)涯