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

Objective-C中的类和对象

作者: lever_xu | 来源:发表于2018-07-19 14:15 被阅读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的底层实现(类与对象)

    概述 对象 Objective-C中,类和对象的底层数据结构,可以参考Objective-C底层数据结构。其中对象...

  • 对象(实例) 类与元类

    在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有is...

  • iOS 面试题总结

    isa指针: 在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象...

  • Objective-C isa 指针

    在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针...

  • Category 为什么不能添加实例变量

    在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。任何对象都有isa指针...

  • iOS - Objective-C 对象的分类以及 isa、su

    在 Objective-C 中对象分为三类: Instance 对象(实例对象) Class 对象(类对象) Me...

  • 【iOS】类别和类拓展

    类别(Category)和类拓展(Extension)是Objective-C中独有的用于拓展类对象的机制。 类别...

  • OC对象的分类

    Objective-C对象的分类 Objective-C中的对象也叫OC对象,主要分为三类:1.实例对象(inst...

  • iOS isa指针

    在Objective-C中,任何类的定义都是对象。类和类的实例没有任何本质上的区别。任何对象都有isa指针。 is...

  • iOS 底层原理

    1.isa指针 在Objective-C中,任何类的定义都是对象。类和类的实例(对象)没有任何本质上的区别。 is...

网友评论

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

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