iOS runtime
ios runtime
什么是runtime
原理/过程/作用/注意事项
objective 客观的,目标的
objective-c :扩充c的面相对象编程语言,是用C写的运行库。在C的基础上增加了面向对象编程语言的特性和消息机制。
运行时机制就是运行时确定类的对象,运行时确定调用的方法,运行时为程序加载新的模块
其运行时就是动态性的特点,运行时机制的根本就是objective-c的类的数据结构。
因为objective-c是基于C来写的,所以其对象其实是由C的结构体来实现的。如下:
id 类型的定义
objc.h
/// A pointer to an instance of a class.
typedef struct objc_object *id;
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa ;
};
runtime.h
struct objc_class {
Class isa ; //指向对象的类,而Class中也会有一个isa指针,指向meteClass元类,元类保存了类方法的列表。当类方法被调用时,先会从本身查找类方法的实现,如果没有,元类会向它父类查找该方法。
Class super_class ;//指向其父类
const char *name ;// 类名/对象名
long version ; //版本
long info ; //信息
long instance_size ; //大小
struct objc_ivar_list *ivars ;//属性列表
struct objc_method_list **methodLists ;//方法列表 它将方法选择器和方法实现地址联系起来。methodLists 是指向 ·objc_method_list 指针的指针,也就是说可以动态修改 *methodLists 的值来添加成员方法,这也是 Category 实现的原理,同样解释了 Category 不能添加属性的原因。
struct objc_cache *cache ;//缓存 统会把被调用的方法存到 cache 中(理论上讲一个方法如果被调用,那么它有可能今后还会被调用),下次查找的时候效率更高
struct objc_protocol_list *protocols ;协议列表
};
如下创建三个类,D1继承NSObject,D2继承D1,D3继承D2。

现在创建一个D3 类的对象,那么他们的isa指针和super_class指针指向就如下图:

实例对象
首先,Runtime 系统会把方法调用转化为消息发送,即 objc_msgSend,并且把方法的调用者,和方法选择器,当做参数传递过去.
此时,方法的调用者会通过 isa 指针来找到其所属的类,然后在 cache 或者 methodLists 中查找该方法,找得到就跳到对应的方法去执行。
如果在类中没有找到该方法,则通过 super_class 往上一级超类查找(如果一直找到 NSObject 都没有找到该方法的话,这种情况,我们放到后面消息转发的时候再说)。
前面我们说 methodLists 指向该类的实例方法列表,实例方法即-方法,那么类方法(+方法)存储在哪儿呢?类方法被存储在元类中,Class 通过 isa 指针即可找到其所属的元类。
作用:
1.消息发送
使用方法 objc_msgSend
Persion *onePer = [[Persion alloc] init];
//[onePer printPersionName:@"小明"];
objc_msgSend(onePer,@selector(printPersionName:),@"小明");
objc_msgSend(onePer, @selector(eat:say:), @"苹果", @"Hello");
2.交换方法的实现
使用到的方法:
class_getInstanceMethod
method_exchangeImplementations
//动态交换两个方法的实现
+ (void)load {
//class_getInstanceMethod(__unsafe_unretained Class cls, SEL name)获取对象方法
//Class:获取哪个类方法
//SEL:获取方法编号,根据SEL就能去对应的类找方法
Method function1 = class_getInstanceMethod([Persion class], @selector(printPersionName:));
Method function2 = class_getInstanceMethod([Persion class], @selector(myPrintPersionName:));
method_exchangeImplementations(function1, function2);
}
- (void)myPrintPersionName:(NSString *)name {
NSLog(@"%s --- %@", __func__, name);
//在这个方法中调用自己(myPrintPersionName)时其实就是调用被替换的那个方法。
}
- 添加属性
使用到的方法
objc_setAssociatedObject
objc_getAssociatedObject
.h
@property(nonatomic, strong)NSString *phoneNumber;
.m
//动态添加属性
-(void)setPhoneNumber:(NSString *)phoneNumber {
//OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
// object:给哪个对象添加属性
// key:属性名,根据key去获取关联的对象,void * 就是id
// value:关联的值,属性名
// policy:策略
objc_setAssociatedObject(self, @"phoneNumber", phoneNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)phoneNumber {
return objc_getAssociatedObject(self, @"phoneNumber");
}
4.获取类信息
//获取类的所有方法
+ (void)LogAllMethodsFromClass:(id)obj
{
u_int count;
//class_copyMethodList 获取类的所有方法列表
Method *mothList_f = class_copyMethodList([obj class],&count) ;
for (int i = 0; i < count; i++) {
Method temp_f = mothList_f[i];
// method_getImplementation 由Method得到IMP函数指针
IMP imp_f = method_getImplementation(temp_f);
// method_getName由Method得到SEL
SEL name_f = method_getName(temp_f);
const char * name_s = sel_getName(name_f);
// method_getNumberOfArguments 由Method得到参数个数
int arguments = method_getNumberOfArguments(temp_f);
// method_getTypeEncoding 由Method得到Encoding 类型
const char * encoding = method_getTypeEncoding(temp_f);
NSLog(@"方法名:%@\n,参数个数:%d\n,编码方式:%@\n",[NSString stringWithUTF8String:name_s],
arguments,[NSString stringWithUTF8String:encoding]);
}
free(mothList_f);
}
//获取类的所有属性 @ property 声明的
+ (NSArray *)getAllProperties:(id)obj
{
u_int count;
//使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称
objc_property_t *properties =class_copyPropertyList([obj class], &count);
NSMutableArray *propertiesArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i<count; i++)
{
const char* propertyName =property_getName(properties[i]);
NSLog(@"属性%@\n",[NSString stringWithUTF8String: propertyName]);
[propertiesArray addObject: [NSString stringWithUTF8String: propertyName]];
}
free(properties);
return propertiesArray;
}
// 成员变量 ,类的所有属性和变量
Ivar *ivars = class_copyIvarList(cls, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSLog(@"instance variable's name: %s at index: %d", ivar_getName(ivar), i);
}
free(ivars);
5.动态添加方法
使用方法
class_addMethod

void myEat(id self, SEL _cmd, id param) {
NSLog(@"%s -- %@ eat", __func__, param);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat:)) {
/*
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,
const char *types)
// 第一个参数:给哪个类添加方法
// 第二个参数:添加方法的方法编号
// 第三个参数:添加方法的函数实现(函数地址)
// 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
cls:给哪个类添加方法
SEL:添加方法的编号是什么
IMP:方法实现,函数入口,函数名
types:方法类型 (返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
*/
class_addMethod(self, sel, (IMP)myEat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
参考:
https://southpeak.github.io/2014/10/25/objective-c-runtime-1/