RunTime简称运行时。就是系统在运行的时候的一些机制,其中最主要的是消息机制。对于C语言,函数的调用在编译的时候会决定调用哪个函数。编译完成之后直接顺序执行,无任何二义性。OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编 译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。只有在真正运行的时候才会根据函数的名称找 到对应的函数来调用。
那OC是怎么实现动态调用的呢?下面我们来看看OC通过发送消息来达到动态调用的秘密。假如在OC中写了这样的一个代码:
Person *p = [[Person alloc] init];
对于这样一个简单的调用。在编译时RunTime会将上述代码转化成:
Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
在NSObjcet中存在一个Class的isa指针。然后我们看看Class:
typedef struct objc_class *Class;
struct objc_class {
Class isa; // 指向metaclass
Class super_class ; // 指向其父类
const char *name ; // 类名
long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数 class_setVersion和class_getVersion进行修改、读取
long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);
struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址
struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;
struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;
struct objc_protocol_list *protocols; // 存储该类遵守的协议
}
2.runtime有什么作用?
runtime属于OC的底层操作,可以实现使用OC语法无法实现的方法和功能:获取一个类所有的方法,包含私有方法
// 获取类中的方法
- (void)getMethodList
{
int a = 0;
int b = 10;
int c = 2;
int arr[] = {a,b,c};
int *p;
p = arr;
NSLog(@"%d %d",arr[1],p[1]);
// 获取Person类中所有方法
// 参数一:获取哪个类的方法列表
// 参数二:方法列表总数
unsigned int count = 0;
// 调用完这个方法,count就有值,记录方法列表总数
// 获取仅仅是当前类
// 返回指向方法列表数组
Method *methodList = class_copyMethodList([Person class], &count);
// 2 0 1
for (int i = 0; i < count; i++) {
// 取出对应的方法
Method method = methodList[i];
// 获取方法名(方法编号)
SEL methodSel = method_getName(method);
NSLog(@"%@",NSStringFromSelector(methodSel));
}
}
获取类所有的属性,包含私有属性
其实打断点也可以获取一个类的所有属性
方法交换
// 加载分类进内存的时候调用
+ (void)load
{
NSLog(@"%s",__func__);
// 交换imageNamed和yc_imageNamed实现
// 获取imageNamed方法
// Class:获取哪个类方法
// SEL:获取方法方法编号
Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
// 获取yc_imageNamed方法
Method yc_imageNameMethod = class_getClassMethod(self, @selector(yc_imageNamed:));
// 交换方法实现
method_exchangeImplementations(imageNameMethod, yc_imageNameMethod);
}
+ (instancetype)yc_imageNamed:(NSString *)name
{
// 加载图片
UIImage *image = [UIImage yc_imageNamed:name];
if (image == nil) {
NSLog(@"加载失败");
}else{
NSLog(@"加载成功");
}
return image;
}
动态添加方法
#import "Person.h"#import@implementation Person
// 没有返回值,没有参数
// void(*)()
// 完整方法实现
void eat(id self, SEL _cmd, NSString *str)
{
NSLog(@"动态添加的吃方法 %@",str);
}
// 注意:任何一个方法都有两个隐式参数,self,_cmd
// 动态添加方法步骤
// 作用:处理未实现方法
// 什么时候调用:调用了一个未实现的方法就会调用这个方法
// 参数:未实现方法的方法编号
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"%@",NSStringFromSelector(sel));
// 动态添加eat
if (sel == @selector(eat:)) {
// 动态添加方法
// Class:给哪个类添加方法
// SEL:添加什么方法
// IMP:方法实现,函数入口 -> 函数名
// type:方法类型 v:void @:id :->SEL
class_addMethod(self, sel, (IMP)eat, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
动态添加属性
#import "NSObject+Property.h"#import@implementation NSObject (Property)
- (void)setName:(NSString *)name
{
// name保存到对应对象
// 动态添加属性
// object:给那个对象添加属性
// key:属性名
// value:把什么对象保存起来
// policy:策略,用什么策略
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name
{
return objc_getAssociatedObject(self, @"name");
}
@end