分类和扩展

2021-02-01  本文已影响0人  生产八哥

分类Category

分类是运行时动态决议的,扩展是编译期决议的。
通过clang -rewrite-objc main.m -o main.cpp 查看底层编译可以了解代码在C++是怎么处理的。

struct category_t {
    const char *name;
    classref_t cls;
    WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
    WrappedPtr<method_list_t, PtrauthStrip> classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    
    protocol_list_t *protocolsForMeta(bool isMeta) {
        if (isMeta) return nullptr;
        else return protocols;
    }
};

从底层查看分类的结构体,会发现其有实例/类方法列表,还有属性列表,但属性不会自动生成get set方法。

在面向对象(oop)的编程语言中,每一个对象都是某个类的实例。在 Objective-C中,所有对象的本质都是一个 objc_object 结构体,且每个实例对象的第一个成员变量都是 Class isa,指向该对象对应的类,每一个类描述了一系列它的实例对象的信息,包括对象占用内存大小、成员变量列表、该对象能执行的函数列表...等。

为什么分类不能添加属性?其实可以添加,只是不会生成get、set方法

category 定义中可以看出 category 可以添加实例方法、类方法甚至可以实现协议、添加属性,同时也看到不能添加成员变量。 那为什么说不能添加属性呢?实际上,category 允许添加属性,可以使用 @property 添加,但是能添加 @property 不代表可以添加 “完整版的” 属性,通常我们说的添加属性是指编译器为我们生成了对应的成员变量和对应的 setter 和 getter 方法来存取属性。在 category 中虽说可以书写 @property,但是不会生成 _成员变量,也不会生成所添加属性的 gettersetter 方法的实现,所以尽管添加了属性,也无法使用点语法调用 setter 和 getter 方法。(实际上,点语法可以写,只不过在运行时调用到这个方法时会报找不到方法的错误: unrecognized selector sent to instance ....)。我们此时可以通过 associated object 来为属性手动实现 setter 和 getter 存取方法。

关键原因:分类中声明的属性默认@dynamic修饰。@dynamic意思是告诉系统set get方法由用户实现,即动态实现。如果在分类的.m里手动添加set get方法。class_copyPropertyList可以拿到分类属性名,即可以生成属性名,但不会生成ivar成员变量,因为ivar是生成setget方法的关键所以也没有setget方法,所以相当于不能添加属性。实例变量是成员变量的一种特殊情况。

广义原因:类的成员变量布局以及其实例对象大小在编译时就已确定,设想一下,如果 Objective-C 中允许给一个类动态添加成员变量,会带来一个问题:为基类动态增加成员变量会导致所有已创建出的子类实例都无法使用。 我们所说的 “类的实例”(对象),指的是一块内存区域,里面存储了 isa 指针和所有的成员变量,是类alloc出来,并且已经分配好了内存布局(内存对齐等一系列优化)且和isa关联后的产物。所以假如允许动态修改类已固定的成员变量的布局,那么那些已经创建出的对象就不符合类的定义了,就变成无效对象了。而方法的定义都是在类对象或元类对象中的,不管如何增删方法,都不会影响对象的内存布局,已经创建出的对象仍然可以正常使用。


解决方案:可以用关联来给分类动态实现属性效果。

Runtime中存在一个类型为AssociationHashMap的哈希映射表保存着对象动态添加的属性,每个对象以自身地址为key维护着一个绑定属性表,我们动态添加的属性就都存储在这个表里,这也是动态添加property能成功的基础

load_images阶段系统调用+load方法时:分类中load方法不会覆盖本类的load方法,且是主类先执行load。
用户主动调用+load方法时:优先执行分类的load。
所以+load方法可能会执行多次。那么在+load的逻辑如果严谨点的话,需要dispatch_once保证执行一次。
initialize是类第一次接收到消息的时候调用

扩展Extension

上一篇 下一篇

猜你喜欢

热点阅读