iOS - Runtime详解

要点:

1.什么是Runtime

2. iOS RunTime解析

3. Method-swizzling

 

 

什么是Runtime

 

Runtime,即运行时,通常我们说的Runtime是指程序的后台的运行环境

 

传统的面向过程的语言开发,例如c语言编译器会直接把代码变成最底层的机器指令,变量、函数都变成地址偏移。程序运行时CPU只要一条条的处理就行了。

 

这种机制比较死板,也缺乏跨平台特性,因此现在的大多数编程语言已经不再直接生成机器代码,而且是使用一套托管环境。编译器会把代码编译成一种类似于脚本的中间代码,在运行时再由托管程序来执行。

 

Objective-C诡异的地方在于这一套中间语言API是透明的,我们可以通过调用这套API来做一些超乎想相的事情。

 

iOS RunTime解析

 

1. 了解NSObject

 

要了解iOSrunTime首先就要了解下NSObject,它是OC所有类的基类,先看下他的定义,重点如下,

 

@interface NSObject <NSObject> {  

    Class   isa;  

}  

 

可以看到NSObject核心其实就是定义了一个叫isaClass变量,那么这个Class是什么呢?再往下看Class的定义,

 

typedef struct objc_class *Class;  

 

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;

 

#if !__OBJC2__

        Classsuper_class ; // 指向父类

        constchar *name ; // 类名

        long version; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion或者class_getVersion进行修改、读取

        long info; //一些标识信息,CLS_CLASS(0x1L) 表示该类为普通 class ,其中包含实例方法和变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;

        longinstance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);

       struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址

        structobjc_method_list **methodLists ; // info 的一些标志位有关,CLS_CLASS (0x1L),则存储实例方法,如CLS_META (0x2L),则存储类方法;

       struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率;

       struct objc_protocol_list *protocols; // 存储该类声明遵守的协议

#endif

 

} OBJC2_UNAVAILABLE;

 

很明显NSObject的核心其实是一个叫objc_class的数据结构,其中保存着关键的数据,其中super_class是其父类的指针,name是类名,ivars是这个类的变量列表,methodLists是函数列表,cache是方法的缓存,protocols是一个protocollist


所以OC代码执行的时候,首先会去找函数cache,里头有没有对应函数,如果找不到,会去找methodLists列表中是否含有对应的函数,如果再没有就会去找super_class 中的cache,然后.....

 

实际上我们在进行函数调用时runtime会调用一个叫objc_msgSend的函数,这个函数封装了对类中方法的查找操作。

 

具体的工作过程类似于这样,

首先我们需要一个Selector,它的定义如下,

 

// Anopaque type that represents a method selector.

typedefstruct objc_selector *SEL;

 

其实就是一个变量或函数的唯一标示符(id),所有的查找都是通过它来进行的。

下面是苹果Objective-C RuntimeReference文档中对它们的描述:

选择器(typedefstruct objc_selector *SEL):选择器用于表示一个方法在运行时的名字,一个方法的选择器是一个注册到(或映射到)Objective-C运行时中的C字符串,它是由编译器生成并在类加载的时候被运行时系统自动映射。

 

有了sel我们就可以找到一个IMP

IMP method = objc_msg_lookup (class->isa, sel);

 

IMP的定义如下,

typedef id (*IMP)(id, SEL, ...)

 

这种数据类型其实就是实现某个方法的函数开始位置的指针,函数使用的是基于当前CPU架构的标准C调用规约。第一个参数是指向self的指针(也就是该类的某个实例的内存空间,或者对于类方法来说,是指向元类(metaclass)的指针)。第二个参数是方法的选择器,后面跟的都是参数。

 

最后调用它,完毕。

method(person, sel, @"p1", @"p2");

 

 

2.Objective-C Runtime Reference

 

Objective-C提供了一整套的API用来做我刚刚提到那些事情,通过反射查找函数、变量,修改函数变量列表等等,你几乎可以改变一切,下面是几个典型的函数。

 

发消息:objc_msgSend

带返回值的消息:voidobjc_msgSend_stret ( id self, SEL op, ... );

发消息给父类:objc_msgSendSuper

增加函数:class_addMethod

增加实例变量:class_addIvar

增加属性:@dynamic标签,或者class_addMethod,因为属性其实就是由gettersetter函数组成

增加Protocol:class_addProtocol

获取函数列表及每个函数的信息(函数指针、函数名等等):class_getClassMethod

获取属性列表及每个属性的信息:class_copyPropertyList

获取类本身的信息,如类名等:class_getName

获取变量列表及变量信息:class_copyIvarList

将实例替换成另一个类:object_setClass

将函数替换成一个函数实现:class_replaceMethod

 

详见Apple的文档:

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html

 

Method-swizzling

 

在计算机学科中,指针变换(pointer swizzling)是指将基于名字或位置的引用转变为直接的指针引用,Method Swizzling与之类似,指的是改变一个已存在的选择器对应的实现的过程。


下面是一个典型的例子,

@implementation UIViewController (Tracking) 
 
+ (void)load { 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        Class class = [self class]; 
 
        // When swizzling a class method, use the following: 
        // Class class = object_getClass((id)self); 
 
        SEL originalSelector = @selector(viewWillAppear:); 
        SEL swizzledSelector = @selector(xxx_viewWillAppear:); 
 
        Method originalMethod = class_getInstanceMethod(class, originalSelector); 
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 
 
        BOOL didAddMethod = 
            class_addMethod(class, 
                originalSelector, 
                method_getImplementation(swizzledMethod), 
                method_getTypeEncoding(swizzledMethod)); 
 
        if (didAddMethod) { 
            class_replaceMethod(class, 
                swizzledSelector, 
                method_getImplementation(originalMethod), 
                method_getTypeEncoding(originalMethod)); 
        } else { 
            method_exchangeImplementations(originalMethod, swizzledMethod); 
        } 
    }); 
} 
 
#pragma mark - Method Swizzling 
 
- (void)xxx_viewWillAppear:(BOOL)animated { 
    [self xxx_viewWillAppear:animated]; 
    NSLog(@"viewWillAppear: %@", self); 
} 
 
@end 

From <http://www.cocoachina.com/industry/20140225/7880.html> 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值