RunTime运行时是在iOS面试时经常会问到的问题,如果面试官问你有没有用到过RunTime运行时,你该怎么回到呢?
没用过?面试官会觉得你没有钻研底层代码的能力
用过?好吧,你是怎么用的?讲解一下你理解的RunTime
说到RunTime就不得不得说OC的消息机制,我们都知道OC是面向C的,它为C增添了许多面向对象的特性。而OC将很多静态语言在在编译和链接时做的事,交给一位幕后英雄就是RunTime。RunTime是一套纯C的API。
OC的函数调用就是消息发送,在真正运行的时候根据函数名称找到对应的函数来调用。
我们要知道OC的方法由哪几个部分构成呢?
1.SEL:方法编号
2.IMP:方法实现(注意其实它的本质不是函数体,而是函数指针)
我们可以这样理解,一个OC方法就是一本书,SEL方法编号就是这本书的目录标题,IMP方法实现就是对应的页码。而真正的函数体则是页码对应的正文。
消息机制原理就是:对象内部的isa指针根据SEL方法编号去映射表中查找对应的方法实现。
下面说一下简单的RunTime方法应用小例子:
我们创建一个Person类,在.h方法中注册一个eat方法,但是我们不去实现
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)eat;
@end
在ViewController里面调用Person类的eat方法,此时会发生什么事?没错,程序会因为找不到方法的实现而崩溃。
#import "FirstViewController.h"
#import "Person.h"
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person * p =[[Person alloc]init];
[p eat];
}
接下来我们在Person.m文件中,通过runtime消息转发,实例解析方法,来给Person动态添加eat方法的实现。
#import "Person.h"
#import <objc/runtime.h>//引用runtime头文件
@implementation Person
//消息转发
+(BOOL)resolveInstanceMethod:(SEL)sel{//解析实例方法
//添加eat方法!
class_addMethod(self, sel, eat, "");
return [super resolveInstanceMethod:sel];
}
void eat(){
NSLog(@"吃东西");
}
@end
此时,程序就能顺利输出NSLog了。
如果我们需要定义一个有参数的方法
#import <Foundation/Foundation.h>
@interface Person : NSObject
-(void)eatwithobjc:(NSString *)objc;
@end
#import "FirstViewController.h"
#import "Person.h"
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
Person * p =[[Person alloc]init];
[p eatwithobjc:@"汉堡!"];
}
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
//消息转发
+(BOOL)resolveInstanceMethod:(SEL)sel{//解析实例方法
//添加eat方法!
class_addMethod(self, sel, eat, "");
return [super resolveInstanceMethod:sel];
}
void eat(id self,SEL _cmd,NSString * objc){
NSLog(@"吃东西!%@",objc);
}
@end
我们还需要添加隐藏参数id self 和 SEL _cmd