美文网首页
Objective-C Runtimeの类与对象

Objective-C Runtimeの类与对象

作者: 十一月三号 | 来源:发表于2015-12-25 21:13 被阅读33次

    Objective-C是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这样处理也就意味着,它将使我们的代码更有灵活性。比如,我们可以根据我们的意向将消息转发给其它对象,或者去替换我们想要实现的方法等。因为Objective-C“动态化”的内容都是在运行时完成的,所以,OC的运行条件不仅仅要求有帮助我们向机器说话的编译器,还要有让代码随心而动的运行时,而这个运行时就是objc Runtime。它基本上是由C和汇编实现的,它让C具有了面向对象的能力

    Runtime库主要做下面几件事:

    • 封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
    • 找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。

    类与对象

    Objective-C是对C的进一步封装,让C有了面对对象的能力,为什么这么说呢,我们可以看一下下面的这个例子:

    Person *per = [[Person alloc] init];
    

    这是一个很简单的获取实例化对象的方法。
    那么这个语句在经过编译后会变成什么样呢,想看的话我们可以这样做:

    • 打开Xcode,创建一个命令行文件

    • 创建一个.m 文件,在里边定义我们想要的类, 内容如下所示:

    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    
    @property (nonatomic, assign) NSInteger age;
    
    @end
    
    @implementation Person
    
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            Person *p = [[Person alloc] init];
            p.age = 18;
            NSLog(@"%ld", p.age);
        }
        return 0;
    }
    
    
    • 打开命令行,编译main.m为.cpp文件,打开文件可以看到main函数中的实现如下:
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ 
        { 
            __AtAutoreleasePool __autoreleasepool; 
            Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
            ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)p, sel_registerName("setAge:"), (NSInteger)18);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_dd_c58dp2w556l44jqlhxdgthdh0000gn_T_main_449954_mi_0, ((NSInteger (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("age")));
        }
        return 0;
    }
    

    在以上函数中,采用了消息发送机制,可以看到,OC在这里也脱去了它面向对象的外衣,显露除了些许本质。
    以实例化对象p的初始化过程为例,简要分析一下其实现过程:
    首先,在原文件中的

    Person *p = [[Person alloc]init];
    

    先后调用了两个方法,也即发送了两个消息, 对应的,编译后文件就是实现这一过程。

    Person *(*)(id, SEL)(void *)objc_msgSend((id)objc_getClass("Person"), sel_registerName("alloc"));
    

    这一方法对应的就是Person调用的alloc方法,objc_msgSend函数发送alloc消息(需要强转),它将会返回一个结构体(Person的实例化对象)。同理,返回的对象在执行初始化方法init时,需要再次发送消息,也就有了在.cpp文件中我们看到的样子:

    Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
    

    *注:在.m中如果也想如此实现,需要引用<objc/runtime.h>和<objc/message.h>


    数据结构

    Class

    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。
    查看objc/runtime.h中objc_class结构体的定义如下:

    struct objc_class {
    
        Class isa  OBJC_ISA_AVAILABILITY;
    
    
    
    #if !__OBJC2__
    
        Class super_class                       OBJC2_UNAVAILABLE;  // 父类
    
        const char *name                        OBJC2_UNAVAILABLE;  // 类名
    
        long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
    
        long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
    
        long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小
    
        struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表
    
        struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
    
        struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存
    
        struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表
    
    #endif
    
    
    
    } OBJC2_UNAVAILABLE;
    

    MetaClass

    在objc_class的结构体中有isa这么一个字段,在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)。
    当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。
    meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
    再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。
    通过上面的描述,再加上对objc_class结构体中super_class指针的分析,我们就可以描绘出类及相应meta-class类的一个继承体系了,如下图所示:

    继承关系Class与Meta
    我是这样去理解这个图的:
    图中的各个类的关系以及isa指向就如同一个老亚家的家谱以及财产关系,首先图中的subClass可以看做是以诺, 他管理着一个村落中的所有兵器(instance of Class的方法),当村民需要使用时,需要问自己的村长有没有,并在有的情况下调用,如果没有呢?当你要去干掉一条龙,意气风发的去跟村长要剑时,发现他竟然没有,当时你就要崩溃,村长一看,“哎哟哟,你可别死我家门口,我问问我老爹有没有”,然后就给该隐打电话,向他借,再不行就让该隐向亚当借,这要是都没有。。以诺就会告诉村民“行了,大家一起死吧,你要的东西大家都没有”。然后全世界就崩溃掉了(野指针:指向了未识别的方法)。
    这时候你就要问了,那subclass(meta)又是何方神圣呢?好吧,我也不知道她叫啥,是以诺的媳妇就是了,她藏着以诺的小金库,里边装着村长能用的兵器(类方法),当村长要用时就得向她申请,同理,她没有就得向她妈要,在这里因为女子无需上战场,所以她们本身要兵器。。你猜她们想干嘛,所以她们想要兵器直接向RootClass(meta)夏娃说句悄悄话, 夏娃想要兵器也没地要去,看看自己有啥吧。秉承着男传男,女传女的原则,可以很清楚的看清图中的继承关系。等等,天呐,RootClass(meta)继承于RootClass,这是咋回事?嘿嘿,别忘了,夏娃是用亚当的一根肋骨造出来的。而亚当,你能跟造出他那位打通电话吗?

    类与对象的操作函数

    Objective-C Runtime 运行时之一:类与对象

    相关文章

      网友评论

          本文标题:Objective-C Runtimeの类与对象

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