如今看起来Runtime篇整理的少了,有时间再完善下,将就着看吧数组
Objective-C将不少静态语言在编译和连接时期作的工做放在了Runtime运行时处理,能够说Runtime就是Objective-C的幕后工做者。bash
任何方法调用的本质,就是发送了一个消息(用Runtime发送消息,OC底层实现经过Runtime实现)。并发
对象根据方法编号SEL去隐射表查找对应的方法实现。函数
有时候咱们须要对类的方法进行修改,可是又没法拿到源码,咱们即可以经过Runtime来交换方法实现。ui
+ (void)load {
//获取实例方法实现
Method method1 = class_getInstanceMethod(self, @selector(show));
Method method2 = class_getInstanceMethod(self, @selector(ln_show));
//获取类方法实现
// Method method3 = class_getClassMethod(self, @selector(show));
// Method method4 = class_getClassMethod(self, @selector(ln_show));
//交换两个方法的实现
method_exchangeImplementations(method1, method2);
//将method1的实现换成method2
// method_setImplementation(method1, method_getImplementation(method2));
}
- (void)show {
NSLog(@"show person");
}
- (void)ln_show {
NSLog(@"show person exchange");
}
复制代码
实际上并无产生真正的成员变量,经过关联对象来实现,具体参考分类。spa
除了可使用KVC实现外,还能够经过Runtime实现,就是取出全部ivars遍历赋值。但实际状况通常比较复杂:指针
+ (instancetype)modelWithDic:(NSDictionary *)dic {
/*
1.初始化实例对象
*/
id object = [[self alloc] init];
/**
2.获取ivars
class_copyIvarList: 获取类中的全部成员变量
Ivar:成员变量
第一个参数:表示获取哪一个类中的成员变量
第二个参数:表示这个类有多少成员变量,传入一个Int变量地址,会自动给这个变量赋值
返回值Ivar *:指的是一个ivar数组,会把全部成员属性放在一个数组中,经过返回的数组就能所有获取到。
count: 成员变量个数
*/
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList(self, &count);
/*
3.遍历赋值
*/
for(int i = 0; i < count; i++) {
//获取ivar属性
Ivar ivar = ivarList[i];
//获取属性名
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//去掉成员变量的下划线
NSString *key = [ivarName substringFromIndex:1];
//获取dic中对应值
id value = dic[ivarName];
//若是值存在,则赋值
if(value) {
[object setValue:value forKey:ivarName];
}
}
return object;
}
复制代码
若是一个类方法很是多,加载类到内存的时候也比较耗费资源,须要给每一个方法生成映射表,可使用动态给某个类,添加方法解决。code
- (void)viewDidLoad {
[super viewDidLoad];
Person *p = [[Person alloc] init];
// 默认person,没有实现run:方法,能够经过performSelector调用,可是会报错。
// 动态添加方法就不会报错
[p performSelector:@selector(run:) withObject:@10];
}
@implementation Person
// 没有返回值,1个参数
// void,(id,SEL)
void aaa(id self, SEL _cmd, NSNumber *meter) {
NSLog(@"跑了%@米", meter);
}
// 任何方法默认都有两个隐式参数,self,_cmd(当前方法的方法编号)
// 何时调用:只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理
// 做用:动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// [NSStringFromSelector(sel) isEqualToString:@"run"];
if (sel == NSSelectorFromString(@"run:")) {
// 动态添加run方法
// class: 给哪一个类添加方法
// SEL: 添加哪一个方法,即添加方法的方法编号
// IMP: 方法实现 => 函数 => 函数入口 => 函数名(添加方法的函数实现(函数地址))
// type: 方法类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
class_addMethod(self, sel, (IMP)aaa, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
@end
复制代码
在实现encodeObject
和decodeObjectForKey
方法中,咱们通常须要把每一个属性都要写一遍,这样很麻烦,咱们能够经过Runtime来自动化。orm
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for(int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:ivarName];
[aCoder encodeObject:value forKey:ivarName];
}
free(ivarList);
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self == [super init]) {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for(int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [aDecoder decodeObjectForKey:ivarName];
[self setValue:value forKey:ivarName];
}
free(ivarList);
}
return self;
}
复制代码
还有更简便的方法,抽象成宏,参考网上资料。对象
unsigned int count = 0;
//获取属性列表
Ivar *propertyList = class_copyPropertyList([self class], &count);
//获取方法列表
Method *methodList = class_copyMethodList([self class], &count);
//获取成员变量列表
Ivar *ivarList = class_copyIvarList([self class], &count);
//获取协议列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
//获取类方法
Method method1 = class_getClassMethod([self class], @selector(run));
//获取实例方法
Method method2 = class_getInstanceMethod([self class], @selector(tempRun));
//添加方法
class_addMethod([self class], @selector(run), method_getImplementation(method2), method_getTypeEncoding(method2));
//替换方法
class_replaceMethod;
//交换方法
method_exchangeImplementations;
复制代码
当类被引进项目的时候会执行load函数(在main函数开始以前),与这个类是会被用到无关,每一个类的load函数只会被调用一次。因为load函数是自动加载的,不须要调用父类的load函数。
这个方法会在类接收到第一次消息时调用。因为是系统调用,也不须要调用父类方法。