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。

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/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容