您的位置:首页 > 移动开发 > IOS开发

iOS runtime 学习分享

2015-12-18 09:25 323 查看
这是团队小伙伴在内部的一次技术分享, 很开心, 我们团队越来越好了.

iOS runtime 学习分享

Author:Liao Zusheng

申明: 部分资料来自于知名论坛和博客,已在文中给出相关源连接

前言

runtime

对象、消息、运行期之间的联系

从self入手

Class

[Meta Class](#Meta Class)

runtime源码地址

runtime简单的运用实例

对象关联

消息转发

方法调配

其他

总结

前言

在开始分享之前,我也是犹豫了很长时间是否需要在团队里面分享runtime,因为runtime是一柄双刃剑,理解的好,用的好,就能写出一些高效实用的代码,用的不好,也能坑队友于无形之中。触发我想分享runtime的动力是源自于开普勒项目的一次BUG,然后遏制我这个想法的也是开普勒项目的一个BUG,所谓的双刃剑特性在一个小项目展示的淋漓尽致。

学习runtime是程序员进阶的一个必要的过程,不单单是oc,所有语言的编程学习到一定的水平,就要开始研究改语言底层的编译运行机制,我们这里所说的研究,并不是值要灵活使用底层的方法,而是去了解和熟知底层运行的机制,当然我现在的理解也是很浅显的,所以只能分享一些基础的学习内容,后续大家可以自行研究。

runtime

对象、消息、运行期之间的联系

面向对象编程中,对象就是基本构造单元,在对象与对象之间传递数据并执行任务的过程就叫消息传递,一般情况下,我们开发人员做好这两点就可以了,剩下的事情就交给编译器以及底层的基库去完成。

但是还有一个大家平常用不到的环节,当程序运行起来以后,为其提供相关支持的代码,就叫Object-C运行时(runtime)。

从self入手

先抛出一个问题,阅读源码,大家猜一下输出结果,并尝试讲出运行逻辑。

[code]#import <Foundation/Foundation.h>
#import "Father.h"

@interface Son :Father

@end

@implementation Son

- (instancetype)init{
    self = [super init];
    if (self){
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}

@end


编译后的代码

[code]// @implementation Son

static instancetype _I_Son_init(Son * self, SEL _cmd) {
    self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"));
    if (self){
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_ef2320_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_ef2320_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))));
    }
    return self;
}

// @end


上述编译后的代码可以看到两处个关键函数objc_msgSend和objc_msgSendSuper,看一下runtime源码中的这两个函数是如何定义的

[code]#ifndef OBJC_SUPER
#define OBJC_SUPER
struct objc_super {
    id receiver;
#if !defined(__cplusplus)  &&  !__OBJC2__
    Class class;  /* For compatibility with old objc-runtime.h header */
#else
    Class super_class;
#endif
    /* super_class is the first class to search */
};
#endif

/* Basic Messaging Primitives
 *
 * On some architectures, use objc_msgSend_stret for some struct return types.
 * On some architectures, use objc_msgSend_fpret for some float return types.
 * On some architectures, use objc_msgSend_fp2ret for some float return types.
 *
 * These functions must be cast to an appropriate function pointer type 
 * before being called. 
 */
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
    __OSX_***AILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
    __OSX_***AILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);


里面的调用机制大体就是这样了,以上面的分析,回过头来看开始的代码,当输出[self class]和[super class]时,是个怎样的过程。

当使用[self class]时,这时的self是Son,在使用objc_msgSend时,第一个参数是receiver也就是self。第二个参数,要先找到class这个方法的selector,先从Son这个类开始找,没有,然后到Son的父类 Farther中去找,也没有,再去Farther的父类NSObject去找,一层一层向上找之后,在NSObject的类中发现这个class方法,而NSObject的这个class方法,就是返回receiver的类别,所以这里输出Son。

当使用[super class]时,这时要转换成objc_msgSendSuper的方法。先构造objc_super的结构体吧,第一个成员变量就是self, 第二个成员变量是Farther,然后要找class这个selector,先去superClass也就是Farther中去找,没有,然后去Farther的父类中去找,结果还是在NSObject中找到了。然后内部使用函数objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用[self class]调用时相同了,此时的receiver还是Son,所以这里返回的也是Son。

Class

首先来看看id的定义,id是指向一个类实例的指针

[code]typedef struct objc_object *id;


顺藤摸瓜就找到了 Class的定义

[code]typedef struct objc_class *Class;
typedef struct objc_object {
    Class isa;
} *id;


那么objc_class 又是什么呢? 大家会发现 objc_class里面的东西就多了

[code]struct objc_class {
    Class isa;

#if !__OBJC2__
    Class super_class                                        OBJC2_UN***AILABLE;
    const char *name                                         OBJC2_UN***AILABLE;
    long version                                             OBJC2_UN***AILABLE;
    long info                                                OBJC2_UN***AILABLE;
    long instance_size                                       OBJC2_UN***AILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UN***AILABLE;
    struct objc_method_list **methodLists                    OBJC2_UN***AILABLE;
    struct objc_cache *cache                                 OBJC2_UN***AILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UN***AILABLE;
#endif

} OBJC2_UN***AILABLE;


可以看到运行时一个类还关联了它的父类指针,类名,版本,信息,大小,成员变量列表,方法列表,缓存,还有附属的协议。

然后看一下变量和方法的结构体

[code]struct objc_ivar {
    char *ivar_name                                          OBJC2_UN***AILABLE;
    char *ivar_type                                          OBJC2_UN***AILABLE;
    int ivar_offset                                          OBJC2_UN***AILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UN***AILABLE;
#endif
}                                                            OBJC2_UN***AILABLE;

struct objc_ivar_list {
    int ivar_count                                           OBJC2_UN***AILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UN***AILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UN***AILABLE;
}                                                            OBJC2_UN***AILABLE;

struct objc_method {
    SEL method_name                                          OBJC2_UN***AILABLE;
    char *method_types                                       OBJC2_UN***AILABLE;
    IMP method_imp                                           OBJC2_UN***AILABLE;
}                                                            OBJC2_UN***AILABLE;

struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UN***AILABLE;

    int method_count                                         OBJC2_UN***AILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UN***AILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UN***AILABLE;
}                                                            OBJC2_UN***AILABLE;


可以直接理解为objc_ivar_list结构体存储着objc_ivar数组列表,而objc_ivar结构体存储了类的单个成员变量的信息;同理objc_method_list结构体存储着objc_method数组列表,而objc_method结构体存储了类的某个方法的信息。

Meta Class

一个 ObjC 类同时也是一个对象,为了处理类和对象的关系,runtime 库创建了一种叫做元类 (Meta Class) 的东西。当你发出一个类似[NSObject alloc]的消息时,你事实上是把这个消息发给了一个类对象 (Class Object) ,这个类对象必须是一个元类的实例,而这个元类同时也是一个根元类 (root meta class) 的实例。你会说 NSObject 的子类时,你的类就会指向 NSObject 做为其超类。但是所有的元类都指向根元类为其超类。所有的元类的方法列表都有能够响应消息的类方法。所以当 [NSObject alloc] 这条消息发给类对象的时候,objc_msgSend()会去它的元类里面去查找能够响应消息的方法,如果找到了,然后对这个类对象执行方法调用。

换个说法

在Objective-C的设计哲学中,一切都是对象。Class在设计中本身也是一个对象。而这个Class对象的对应的类,我们叫它 Meta Class。即Class结构体中的 isa 指向的就是它的 Meta Class。

多说无益还是通过代码来理解吧

我在网上找了一些资料刨根问底Objective-C Runtime 地址

[code]
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Son class] isKindOfClass:[Son class]];
BOOL res4 = [(id)[Son class] isMemberOfClass:[Son class]];
NSLog(@"%@ %@ %@ %@", res1 ? @"YES" : @"NO" , res2 ? @"YES" : @"NO" , res3 ? @"YES" : @"NO" , res4 ? @"YES" : @"NO");

//结果

2015-12-09 01:37:15.881 Test[26428:716711] YES NO NO NO




每个Class都有一个isa指针指向一个唯一的Meta Class

每一个Meta Class的isa指针都指向最上层的Meta Class(图中的NSObject的Meta Class)

最上层的Meta Class的isa指针指向自己,形成一个回路

每一个Meta Class的super class指针指向它原本Class的 Super Class的Meta Class。但是最上层的Meta Class的 Super Class指向NSObject Class本身

最上层的NSObject Class的super class指向 nil

为了更加清楚的知道整个函数调用过程,编译过后可获得如下代码:

[code]BOOL res1 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isKindOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));

BOOL res2 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("class")));

BOOL res3 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Son"), sel_registerName("class")), sel_registerName("isKindOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Son"), sel_registerName("class")));

BOOL res4 = ((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Son"), sel_registerName("class")), sel_registerName("isMemberOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Son"), sel_registerName("class")));

NSLog((NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_0, res1 ? (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_1 : (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_2 , res2 ? (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_3 : (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_4 , res3 ? (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_5 : (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_6 , res4 ? (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_7 : (NSString *)&__NSConstantStringImpl__var_folders_t6_gy06h2qd2bx0b09430gmfn158szr0t_T_son_73d230_mi_8);


先看前两个调用:

最外层是 objc_msgSend函数,转发消息。

函数第一个参数是 (id)((Class ()(id, SEL))(void )objc_msgSend)((id)objc_getClass(“NSObject”), sel_registerName(“class”))

函数第二个参数是转发的selector

函数第三个参数是 ((Class ()(id, SEL))(void )objc_msgSend)((id)objc_getClass(“NSObject”), sel_registerName(“class”))

我们注意到第一个参数和第三个参数对应重写的是[NSObject class],即使用objc_msgSend向 NSObject Class 发送 @selector(class) 这个消息

打开objc源代码,在 Object.mm 中发现+ (Class)class实现如下:

[code]+ (Class)class {
    return self;
}


所以即返回Class类的对象本身。看如下输出:

[code]NSLog(@"%p", [NSObject class]);
NSLog(@"%p", [NSObject class]);
2014-11-05 18:48:30.939 Test[11682:865988] 0x7fff768d40f0
2014-11-05 18:48:30.940 Test[11682:865988] 0x7fff768d40f0


继续打开objc源代码,在 Object.mm 中,我们发现 isKindOfClass的实现如下:

[code]- (BOOL)isKindOf:aClass
{
    Class cls;
    for (cls = isa; cls; cls = cls->superclass) 
        if (cls == (Class)aClass)
            return YES;
    return NO;
}


对着上面Meta Class的图和实现,我们可以看出

当 NSObject Class对象第一次进行比较时,得到它的isa为 NSObject的Meta Class, 这个时候 NSObject Meta Class 和 NSObject Class不相等。

然后取NSObject 的Meta Class 的Super class,这个时候又变成了 NSObject Class, 所以返回相等。

所以上述第一个输出结果是 YES 。

我们在看下 ‘isMemberOfClass’的实现:

[code]- (BOOL)isMemberOf:aClass
{
    return isa == (Class)aClass;
}


综上所述,当前的 isa 指向 NSObject 的 Meta Class, 所以和 NSObject Class不相等。

所以上述第二个输出结果为 NO 。

继续看后面两个调用:

Son Class 的isa指向的是 Son的Meta Class,和Son Class不相等

Son Meta Class的super class 指向的是 NSObject Meta Class, 和 Son Class不相等

NSObject Meta Class的 super class 指向 NSObject Class,和 Son Class 不相等

NSObject Class 的super class 指向 nil, 和 Son Class不相等

所以后面两个调用的结果都输出为 NO 。

由于时间的原因,runtime的一些核心函数就讲到这里,下面附上runtime的源码

runtime 源码地址

下载地址

runtime简单的运用实例

常见的runtime运用实例推荐使用指数
类对象关联
消息转发**
方法调配不推荐

对象关联

设置关联对象的值

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

属性object、key、value都非常容易理解,oject是需要关联的对象,key,value是一套键值对。objc_AssociationPolicy这个属性大家可能是第一次见,这个是存储策略的值

关联类型等效的@property属性
OBJC_ASSOCIATION_ASSIGNassign
OBJC_ASSOCIATION_RETAIN_NONATOMICnonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMICnonatomic,copy
OBJC_ASSOCIATION_RETAINretain
OBJC_ASSOCIATION_COPYcopy
讲到存储策略,我们来一起回顾一下oc对属性的存储策略,内存管理语义

assgin 一般只对纯量类型的简单赋值操作(NSInterger,int,enum…)。

strong 定义了一种有用关系,为这种属性设置新值时,设置方法会保留新值,并释放旧值,然后再将新值设置上去。

weak 定义了一种非拥有的关系,为这种属性设置新值时,设置方法会既不保留新值,也不会释放旧值,但是在所指对象销毁时,会清空该属性。

copy 和strong表达类似。不同的是设置方法并不保留新值,而是将其拷贝,这样可以保护属性的封装性,不用担心在不知情的情况下被修改,常用于NSString(当然自己写的类可以自己实现copy方法)。

为给定的键和策略为某对象设置关联对象值

objc_getAssociatedObject(id object, const void *key)

移除指定对象的全部关联对象

objc_removeAssociatedObjects(id object)

[code]static const char *UIAlertView_Associated = "UIAlertView_Associated";

- (void)associatedAlert{
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"title"
                                                    message:@"message"
                                                   delegate:self
                                          cancelButtonTitle:@"cancel"
                                          otherButtonTitles:@"confirm", nil];

    void (^block)(NSInteger) = ^(NSInteger buttonIndex){
        if(buttonIndex == 0){
            NSLog(@"do cancel");
        }else{

        }NSLog(@"do confirm");
    };

    objc_setAssociatedObject(alert, UIAlertView_Associated, block, OBJC_ASSOCIATION_COPY_NONATOMIC);

    [alert show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, UIAlertView_Associated);
    if(block)
        block(buttonIndex);

}


消息转发

在讲消息转发前,先要理解什么是消息发送(objc_msgSend),什么时候触发消息发送。

理解动态和静态

用一段代码来理解一下

[code]- (void)doSomeThing:(int)type{
    if(type == 1){
        [self doSend];
    }else{
        [self doReceive];
    }
}
- (void)doSomeThingDynamic:(int)type{
    SEL doAnyThing = nil;
    if(type == 1){
        doAnyThing = @selector(doSend);
    }else{
        doAnyThing = @selector(doReceive);
    }
    if (doAnyThing)
        [self performSelector:doAnyThing];
}
- (void)doReceive{
    NSLog(@"reveive");
}

- (void)doSend{
    NSLog(@"send");
}


大家都知道oc是动态的语言,底层都是c语言实现的代码,所要调用的代码直到运行期才会确定,甚至可以在运行时改变调用方法,这些都是通过消息传递来实现的。

通俗一点Objc 中发送消息是用中括号([])把接收者和消息括起来,而直到运行时才会把消息与方法实现绑定。

消息传递函数原型

[code]id objc_msgSend(id self, SEL op, ...)


这个函数是消息传递机制的核心函数,self 是接收者,op是选择子。

[code]// oc 常见的方法
id returnValue = [someObject messageName:parameter];

//编译器转会称如下函数
id returnValue = objc_msgSend(someObject,
                                    @selector(messageName:),
                                    parameter);


objc_msgSend会依据相关的接受者和选择子来调用适当的方法,为了完成此操作,该方法需要在接受者所属的类中搜寻方法列表,如果找不到就会往父类找,再找不到就会执行消息转发(下面会讲)。可能这里大家会怀疑oc的执行效率,毕竟方法列表是灰常多的。oc在这里这里做了高效的缓存处理,这里我就不细讲了。

我们可以借助关联对象的源码来加深消息发送一下理解

[code]void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
#if SUPPORT_GC
    if (UseGC) {
        if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
            value = objc_msgSend(value, SEL_copy);
        }
        auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
    } else 
#endif
    {
        // Note, creates a retained reference in non-GC.
        _object_set_associative_reference(object, (void *)key, value, policy);
    }
}


消息发送其他相关函数大家感兴趣可以自己去了解一下,如:objc_msgSend_stret,objc_msgSend_fpret,objc_msgSendSuper等

推荐大家读一篇博客[译]Friday Q&A : 动手实现 objc_msgSend

消息转发

前面讲到了对象的消息传递机制,有提到过当对象接收到无法解读的消息后,就会启动消息转发。对象在收到无法解读的消息后,首先将调用所属类的类方法:

[code]+ (BOOL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveClassMethod:(SEL)sel


该方法的参数就是消息传递里面那个未知的选择子,返回值为BOOL类型,表示这个类是否能新增一个实例方法用来处理这个选择子。resolveInstanceMethod处理实例方法,resolveClassMethod处理类方法。

备援接受者

当接收到的选择子不能处理时,还可以转发给其他的接受者处理

[code]- (id)forwardingTargetForSelector:(SEL)aSelector;


该方法的参数就是消息传递里面那个未知的选择子,若当前接收者能找到备援对象,则将其返回,否则返回nil。

完整的消息转发

这是转发的最后一步,首先创建NSInvocation 对象,把与尚未处理的消息有关的全部细节都封装在里面。这个对象包含了选择子、目标、参数等。

[code]- (void)forwardInvocation:(NSInvocation *)anInvocation


完整的消息转发可以参考大神的奇yin巧计博客地址

代码

[code]- (void)forwardInvocation:(NSInvocation *)anInvocation{
if (strcmp(anInvocation.methodSignature.methodReturnType, "@") == 0)
{
anInvocation.selector = @selector(__uxy_nil);
[anInvocation invokeWithTarget:self];
return;
}

for (NSObject *object in XYNullObjects)
{
if ([object respondsToSelector:anInvocation.selector])
{
[anInvocation invokeWithTarget:object];
return;
}
}

[self doesNotRecognizeSelector:anInvocation.selector];
}

- (id)__uxy_nil
{
return nil;
}



消息转发的处理步骤如下:



接受者在每一步都有机会处理消息,越往后处理的代价就越大。所以最好是能在第一步处理完。

方法调配

既然对象的方法都是动态加载,自然就可以动态改变对象执行的方法,但是此方法过于残暴,一般情况下,建议慎重选择。

类的方法列表会把选择子的名称映射到相关的方法实现只想,使动态派发系统能够根据此找到应该调用的方法。这些方法都是用函数指针形式来表示。这种指针叫IMP原型如下

[code]id (*IMP)(id,SEL,)


实现方法调配是通过两个步骤实现的,一是取出类对象方法列表里面相应的方法,二是交换类对象列表里面的方法。

[code]// 获取类对象的方法
Method class_getClassMethod(Class cls, SEL name);
// 交换两个类对象方法
void method_exchangeImplementations(Method m1, Method m2)


实现简单的用例

[code]Method lowMethod = class_getClassMethod([NSString class], @selector(lowercaseString));
Method upMethod = class_getClassMethod([NSString class], @selector(uppercaseString));

method_exchangeImplementations(lowMethod, upMethod);


这样的话,NSString 对象执行 uppercaseString 就变成 uppercaseString,反之亦然。 没见过这么坑人的。所以下面就贴一下大神在开普勒项目如何hook 解决掉别人的bug。

[code]+ (void)load{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keplerAppDidFinishLaunchingNotification)
                                                 name:UIApplicationDidFinishLaunchingNotification
                                               object:nil];

    [self __swizzleInstanceMethodWithClass:NSClassFromString(@"__NSCFString") originalSel:@selector(objectForKey:) replacementSel:@selector(paObjectForKey:)];
}

+ (void)__swizzleInstanceMethodWithClass:(Class)clazz originalSel:(SEL)original replacementSel:(SEL)replacement
{
    Method a = class_getInstanceMethod(clazz, original);
    Method b = class_getInstanceMethod([PAKepler class], replacement);

    if (class_addMethod(clazz, original, method_getImplementation(b), method_getTypeEncoding(b)))
    {
        class_replaceMethod(clazz, replacement, method_getImplementation(a), method_getTypeEncoding(a));
    }
    else
    {
        method_exchangeImplementations(a, b);
    }
}

- (id)paObjectForKey:(id)key
{
    return nil;
}


其他

还有一些比较常用的runtime方法如:

使用class_replaceMethod/class_addMethod函数在运行时对函数进行动态替换或增加新函数

使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称

我就不一一总结了。大家可以自己去做深入学习,多看看runtime源码

总结

揭开runtime神秘面纱之后,我们不难发现,runtime并不是那么的难以理解和使用。但是,这些只是runtime比较粗浅的部分,真正研究透彻还需要花大量的时间,我们应该试着去理解oc runtime的运行机制,底层的设计思想,写出高效且易维护的代码,并不一定要给自己的代码冠上runtime之名,相比之下,花心思研究runtime,还不如去研究接口设计,代码重构,设计模式,编程思想等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: