美文网首页
iOS runtime

iOS runtime

作者: 7分醉 | 来源:发表于2017-12-28 13:07 被阅读24次

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。

1283539-0d4c03820c040246.jpg

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

1283539-ee238b6a64f78358.png

实例对象

首先,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)时其实就是调用被替换的那个方法。
}
  1. 添加属性
    使用到的方法
    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

1180547-1ff94bf6c3890ee2.png
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/

相关文章

网友评论

      本文标题:iOS runtime

      本文链接:https://www.haomeiwen.com/subject/zegslxtx.html