美文网首页
Objective-C中的类和对象

Objective-C中的类和对象

作者: lever_xu | 来源:发表于2018-02-23 10:01 被阅读0次

    id和Class的定义

    runtime里面,声明了idClass的类型,简化一下如下:

    struct objc_class {
        struct objc_class *isa;
    };
    struct objc_object {
        struct objc_class *isa;
    };
    
    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    

    objc中,id代表了一个对象。根据上面的声明,凡是首地址是*isastruct指针,都可以被认为是objc中的对象。运行时可以通过isa指针,查找到该对象是属于什么类(Class)。

    运行时的实现方式

    根据上面的说法,类对象(Class)同样也算是对象,那它的isa又是指向了什么呢?为了了解这些东西是怎么回事,这里写一个简单的类Cat,并且用C重写一遍,看看编译器在底层到底是如何实现的。

    //.h
    @interface Cat : NSObject {
        int age;
        NSString *name;
    }
    
    - (void)nyan1;
    
    + (void)nyan2;
    
    @end
    
    //.m
    @implementation Cat
    
    - (void)nyan1 {
        printf("instance nyan~");
    }
    
    + (void)nyan2 {
        printf("class nyan~");
    }
    
    @end
    

    在终端执行clang -rewrite-objc Cat.m这一条语句,让clang将该类重写为cpp代码,我们就能查看到大概底层的实现机制了。
    rewrite后的代码基本是纯C的,稍微整理一下,可以提取出下面这些信息:

    //class 的实际结构
    struct _class_t {
        struct _class_t *isa;  //isa指针
        struct _class_t *superclass;  //父类
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;  //class包含的信息
    };
    
    //Class包含的信息
    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        unsigned int reserved;
        const unsigned char *ivarLayout;
        const char *name;  //类名
        const struct _method_list_t *baseMethods;  //方法列表
        const struct _objc_protocol_list *baseProtocols;  //协议列表
        const struct _ivar_list_t *ivars;  //ivar列表
        const unsigned char *weakIvarLayout;
        const struct _prop_list_t *properties;  //属性列表
    };
    
    extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
    
    extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Cat __attribute__ ((used, section ("__DATA,__objc_data"))) = {
        0, // &OBJC_METACLASS_$_NSObject,
        0, // &OBJC_METACLASS_$_NSObject,
        0, // (void *)&_objc_empty_cache,
        0, // unused, was (void *)&_objc_empty_vtable,
        &_OBJC_METACLASS_RO_$_Cat,  //包含了类方法等
    };
    
    extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;
    
    extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Cat __attribute__ ((used, section ("__DATA,__objc_data"))) = {
        0, // &OBJC_METACLASS_$_Cat,  //此处isa指向meta-class
        0, // &OBJC_CLASS_$_NSObject,
        0, // (void *)&_objc_empty_cache,
        0, // unused, was (void *)&_objc_empty_vtable,
        &_OBJC_CLASS_RO_$_Cat,  //包含了实例方法 ivar信息等
    };
    typedef struct objc_object Cat;
    

    1、所有Cat的实例的isa都指向了Cat(Class);
    2、Cat(Class)是一个全局变量,其中记录了类名、成员变量信息、property信息、protocol信息和实例方法列表等;
    3、Cat(Class)isa指向了全局变量Cat(meta-class)meta-class里只记录了类名、类方法列表等。

    objc-meta.png

    类的继承

    为了说明方便,这里把上面的例子稍微改一下:NyanCat : Cat : NSObject 这样一个继承树,画出图来就是这样子的:

    image

    可变与不可变

    因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化。所以无法在运行时给对象添加成员变量。
    但同时在 objc_class的结构体中,我们可以发现方法的定义列表时一个名为methodLists的指针的指针,通过修改该指针所指向的指针的值,就可以动态地为某一个类增加成员方法。这也是Category实现的原理。同时也说明了为什么Category只可为对象增加成员方法,却不能增加成员变量。

    需要特别说明一下,通过objc_setAssociatedObject 和 objc_getAssociatedObject方法可以变相地给对象增加成员变量,但由于实现机制不一样,所以并不是真正改变了对象的内存结构。

    系统相关 API 及应用

    Objective-C 提供了以下 API 来动态替换类方法或实例方法的实现:

    • class_replaceMethod 替换类方法的定义
    • method_exchangeImplementations 交换 2 个方法的实现
    • method_setImplementation 设置 1 个方法的实现

    使用场景:

    • class_replaceMethod, 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。
    • method_exchangeImplementations,当需要交换 2 个方法的实现时使用。
    • method_setImplementation 最简单的用法,当仅仅需要为一个方法设置其实现方式时使用。

    使用举例:

    // ImagePickerReplaceMethodsHolder.h
    @interface ImagePickerReplaceMethodsHolder : NSObject
    - (BOOL)shouldAutorotate;
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
    @end
    // ImagePickerReplaceMethodsHolder.m
    @implementation ImagePickerReplaceMethodsHolder
    - (BOOL)shouldAutorotate {
        return NO;
    }
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        return UIInterfaceOrientationPortrait;
    }
    @end
    
    #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
    #define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            [self hackForImagePicker];
        });
    }
    + (void)hackForImagePicker {
        // fix bug of image picker under iOS 6.0
        // http://stackoverflow.com/questions/12522491/crash-on-presenting-uiimagepickercontroller-under-ios-6-0
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")
            && SYSTEM_VERSION_LESS_THAN(@"6.1")) {
            Method oldMethod1 = class_getInstanceMethod([UIImagePickerController class], @selector(shouldAutorotate));
            Method newMethod1 = class_getInstanceMethod([ImagePickerReplaceMethodsHolder class], @selector(shouldAutorotate));
            method_setImplementation(oldMethod1, method_getImplementation(newMethod1));
            Method oldMethod2 = class_getInstanceMethod([UIImagePickerController class], @selector(preferredInterfaceOrientationForPresentation));
            Method newMethod2 = class_getInstanceMethod([ImagePickerReplaceMethodsHolder class], @selector(preferredInterfaceOrientationForPresentation));
            method_setImplementation(oldMethod2, method_getImplementation(newMethod2));
        }
    }
    

    KVO和KVC的实现原理

    • KVO

    当你观察一个对象时,一个新的类会被动态创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。重写的 setter 方法会负责在调用原 setter 方法之前和之后,通知所有观察对象:值的更改。最后通过isa 混写isa-swizzling) 把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。我画了一张示意图,如下所示:

    KVC 支持实例变量,KVO 只能手动支持手动设定实例变量的KVO实现监听

    相关文章

      网友评论

          本文标题:Objective-C中的类和对象

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