Objective-C (二)扩展与分类

分类

1.分类数据结构

  struct objc_category {
      const char *name;
      classref_t cls;
      struct method_list_t *instanceMethods;
      struct method_list_t *classMethods;
      struct protocol_list_t *protocols;
      struct property_list_t *instanceProperties;
      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);
  }

2.分类添加方法的原理:

  • 编译期过程
    1. 编译器生成实例方法列表OBJC$CATEGORY_INSTANCE_METHODS"className"$"categoryName"和属性列表OBJC$PROP_LIST"className"$"categoryName";用以填充我们在类中定义的方法跟属性。
    2. 编译器生成category本身,并用前面生成的方法列表跟属性列表初始化category本身
    3. 编译器在DATA段下的objc_catlist section里保存了一个大小为1的category_t的数组L_OBJC_LABELCATEGORY$,用于运行期的加载.(关于DATA段以及以下dyld的内容,会在mach-O的篇章介绍)
  • 运行期过程
    1. 拿到catlist上的category_t数组,把category_t上的实例方法,协议以及属性添加到类上
    2. 把category_t的类方法和协议添加到类的metaclass上
  • 运行期添加分类过程
    1. addUnattachedCategoryForClass将类和category做一个映射
    2. remethodizeClass负责处理添加事宜
    3. 添加实例方法会调用attachCategoryMethods,它将所有category的实例方法列表拼成一个大的实例方法列表,然后转交给attachMethodList

3.添加属性
我们都知道无法给分类添加成员变量,因为category并不会为我们生成set,get方法,这个时候可以用关联对象来实现

  objc_setAssociatedObject();
  objc_getAssociatedObject();

关联对象的原理分析,内存管理可看以下章节

4.load方法与intialize方法的比较

load方法与intialize方法的比较

5.补充的点

  • 分类的方法只会添加,不会覆盖原先的方法,调用同名方法时调用的是最后编译的分类文件中的方法实现。
  • intialize方法会先调用父类,再子类,所以子类未实现时,父类会被多次调用。

类扩展与分类区别

  • 扩展可以当成一个匿名的类,但是必须要有该类的源码的时候才能编写扩展。(扩展不能拥有独立的实现部分)
  • 类扩展是在编译期添加到类中,而分类是在运行时添加
  • 写在.m的类扩展方法与属性是私有的

关联对象

  • 关联对象保存在什么地方

我们先看看函数_object_set_associative_reference

 void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                _class_setInstancesHaveAssociatedObjects(_object_getClass(object));
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
 

从以上代码可知关联对象由AssociationsManager管理,定义如下

class AssociationsManager {
    static OSSpinLock _lock;
    static AssociationsHashMap *_map;               // associative references:  object pointer -> PtrPtrHashMap.
public:
    AssociationsManager()   { OSSpinLockLock(&_lock); }
    ~AssociationsManager()  { OSSpinLockUnlock(&_lock); }
    
    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};

AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。

  • 关联对象的内存管理
void *objc_destructInstance(id obj) 
{
    if (obj) {
        Class isa_gen = _object_getClass(obj);
        class_t *isa = newcls(isa_gen);

        // Read all of the flags at once for performance.
        bool cxx = hasCxxStructors(isa);
        bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);
        
        if (!UseGC) objc_clear_deallocating(obj);
    }

    return obj;
}

runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容