OC就是运行时机制。在真正运行的时候发送消息,去决定调用哪个函数。
进入.m 文件 使用终端命令:clang -rewrite-objc xxx.m
可以看到编译后的xxx.cpp(C++文件)
1. Runtime 动态添加方法(消息转发机制)
//动态方法解析
//这是NSObject根类提供的类方法,调用时机为当被调用的方法实现部分没有找到,而消息转发机制启动之前的这个中间时刻。
//_cmd 表示方法的编号,打印结果为当前执行的方法名
// type: 方法类型:void用v来表示,id参数用@来表示,SEL用:来表示
+ (BOOL)resolveInstanceMethod:(SEL)sel{
//Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,const char * _Nullable types
class_addMethod(self, sel, eat, "v@:");
return [super resolveInstanceMethod:sel];
}
// objc_msgSend(person,@selector(eat),@"汉堡");消息转发机制
//方法有2个隐藏的函数,id sel;
void eat(id self,SEL _cmd,NSString *obj){
NSLog(@"chichichi-%@",obj);
}
//performSelector 可以保证类如果未实现方法的情况下,程序不崩溃。
Person *person = [[Person alloc]init];
// objc_msgSend(person,@selector(eat),@"汉堡");消息转发机制
[person performSelector:@selector(eat:) withObject:@"汉堡"];
2 使用Runtime 归档(归档类+父类)
#import <Foundation/Foundation.h>
#import "Pepoles.h"
@interface Person : Pepoles<NSCoding>
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSNumber *age;
//@property(nonatomic,assign)int age;
@end
#import "Person.h"
#import <objc/runtime.h>
//使用runtime来存数据
@implementation Person
//方法一:使用 object_setIvar /object_getIvar 这个方法多用在类的属性个数 知道的情况。
注意:类的属性申明时不能出现C的基础类型,比如Person的age 不能申明为int age;
object_getIvar 返回什么取决于 ivar 是什么,并不是你看到的那种返回的是id 类型。
ivar 结构体指针,object_getIvar 的返回id 任意类型的对象指针。如果是int 的会造成指向一个int 类型的地址去操作对象的事情,然后就炸了
方法二:KVC方法
// 取
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
if (self == [super init]) {
Person *obj = [[Person alloc]init];
Class cls = obj.class;
while ([NSStringFromClass(cls) isEqualToString:@"NSObject"]!=YES) {
unsigned int count = 0;
//获取类中所有的实例变量。记住:不包含父类的
Ivar *list = class_copyIvarList(cls, &count);
for (int i = 0; i<count; i++) {
Ivar ivar = list[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
//设置实例对象中Ivar的值
//void object_setIvar(id obj, Ivar i
id value = [aDecoder decodeObjectForKey:key];
// [self setValue:value forKey:key]; 方法二KVC 存值
object_setIvar(self, ivar, value);方法一
}
//获取父类
cls = class_getSuperclass(cls);
free(list);
}
// self.age = [aDecoder decodeIntForKey:@"age"];
// self.name = [aDecoder decodeObjectForKey:@"name"];
}
return self;
}
//存
- (void)encodeWithCoder:(NSCoder *)deCoder{
Person *obj = [[Person alloc]init];
Class cls = obj.class;
while ([NSStringFromClass(cls) isEqualToString:@"NSObject"]!=YES) {
unsigned int count = 0;
Ivar *list = class_copyIvarList(cls, &count);
for (int i = 0; i<count; i++) {
Ivar ivar = list[I];
//获得成员变量的名字
const char * name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
//id value = [self valueForKey:key];KVC 取值
id value = object_getIvar(self, ivar);
[deCoder encodeObject:value forKey:key];
}
cls = class_getSuperclass(cls);
//结构体指针数组 要手动释放。
free(list);
}
// [deCoder encodeObject:self.name forKey:@"name"];
// [deCoder encodeInt:self.age forKey:@"age"];
}
@end
- (void)setAge:(int)age{
objc_setAssociatedObject(self, @"a", @(age), OBJC_ASSOCIATION_ASSIGN);
}
- (int)age{
return [objc_getAssociatedObject(self, @"a") intValue];
}
4.Runtime 方法交换,传说中的黑魔法Method Swizzling(写在分类中)
SEL 方法的ID,IMP 函数指针,指向了方法的实现,可以吧SEL 当作key IMP value
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
//class_getInstanceMethod 获取实例方法
Method originalM = class_getInstanceMethod(class, originalSelector);
Method swizzleM = class_getInstanceMethod(class, swizzledSelector);
//方法交换
method_exchangeImplementations(originalMethod, swizzledMethod);