美文网首页
Category-初探

Category-初探

作者: yahibo | 来源:发表于2019-09-17 11:43 被阅读0次

    参考:objc4-750源码编译

    什么是Category?

    分类是Objective-C 2.0之后新加的语言特性,一个特殊类,主要有一下作用:

    • 在不改变原有类的前提下为已存在的类扩展新方法
    • 拆分类文件做功能分类,便于管理
    • 引用即用
    • 有多继承的效果

    分类不能添加成员变量,可以添加属性,但是系统不会生成属性对应的getter、setter方法,因此属性是无效的,需要我们手动添加getter、setter方法,通过关联属性达到扩展属性的效果。

    什么是Extension?

    Extension一般称为扩展、延展、匿名分类,应用中不仅能够声明方法,还能声明属性,成员变量。

    分类应用

    1、NSDate

    #pragma mark - 格式化时间
    +(NSString*)dateFormat:(NSString*)format seconds:(NSTimeInterval)secs{
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [formatter setTimeZone:nil];
        [formatter setDateFormat:format];
         NSString *timeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:secs]];
        return timeStr;
    }
    #pragma mark - 将字符串转换成时间戳
    +(NSTimeInterval)getTimeIntervalFormat:(NSString *)format TimeString:(NSString *)timeString{
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:format];
        NSDate *date = [formatter dateFromString:timeString];
        NSInteger timeSp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue];
        return timeSp;
    }
    #pragma mark - 根据时间戳判断这一天是周几
    +(NSString*)getWeekSeconds:(NSTimeInterval)secs{
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
        [formatter setTimeZone:nil];
        [formatter setDateFormat:@"EEEE"];
        NSString *timeStr = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:secs]];
        return timeStr;
    }
    

    2、NSString

    #pragma mark - 判断字符串是否为空
    + (BOOL)isEmptyString:(NSString*)string{
        if ([string isKindOfClass:[NSNull class]]) {
            return YES ;
        }
        if([string isKindOfClass:[NSNumber class]]){
            string = [NSString stringWithFormat:@"%@%@",string,@""];
        }
        if (string == nil) {
            return YES ;
        }
        if (string.length == 0 ||
            [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length == 0){
            return YES ;
        }
        return NO ;
    }
    #pragma mark - json格式转换为json字符串
    +(NSString *)jsonToJsonString:(id)obj{
        NSError *error = nil;
        NSData *jsonData = [NSJSONSerialization
                            dataWithJSONObject:obj options:NSJSONWritingPrettyPrinted error:&error];
        NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
        jsonString = [jsonString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        jsonString = [jsonString stringByReplacingOccurrencesOfString:@" " withString:@""];
        return jsonString;
    }
    #pragma mark- 获取字符串字节数
    +(NSUInteger)textByteLength: (NSString *) text{
        NSUInteger asciiLength = 0;
        for (NSUInteger i = 0; i < text.length; i++) {
            unichar uc = [text characterAtIndex: I];
            asciiLength += isascii(uc) ? 1 : 2;
        }
        NSUInteger unicodeLength = asciiLength;
        return unicodeLength;
    }
    #pragma mark - json字符串转换为json
    -(id)jsonStringToJson{
        NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
        id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        return obj;
    }
    

    还有加密、手机号验证、身份证验证等等。

    3、UIViewController

    + (void)load {
        //我们只有在开发的时候才需要查看哪个viewController将出现
        //所以在release模式下就没必要进行方法的交换
    #ifdef DEBUG
        //原本的viewWillAppear方法
        Method viewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:));
        //需要替换成 能够输出日志的viewWillAppear
        Method logViewWillAppear = class_getInstanceMethod(self, @selector(logViewWillAppear:));
        //两方法进行交换
        method_exchangeImplementations(viewWillAppear, logViewWillAppear);
        Method viewWillDisappear = class_getInstanceMethod(self,@selector(viewWillDisappear:));
        Method logViewWillDisappear = class_getInstanceMethod(self,@selector(logViewWillDisappear:));
        method_exchangeImplementations(viewWillDisappear,logViewWillDisappear);
        
    #endif
    }
    -(void)logViewWillDisappear:(BOOL)animated{
        NSString *className = NSStringFromClass([self class]);
        //在这里,你可以进行过滤操作,指定哪些viewController需要打印,哪些不需要打印
        if ([className hasPrefix:@"UI"] == NO) {
            NSLog(@"%@ will disappear",className);
        }
        //下面方法的调用,其实是调用viewWillAppear
        [self logViewWillDisappear:animated];
    }
    - (void)logViewWillAppear:(BOOL)animated {
        NSString *className = NSStringFromClass([self class]);
        //在这里,你可以进行过滤操作,指定哪些viewController需要打印,哪些不需要打印
        if ([className hasPrefix:@"UI"] == NO) {
            NSLog(@"%@ will appear",className);
        }
        //下面方法的调用,其实是调用viewWillAppear
        [self logViewWillAppear:animated];
    }
    
    • 实现了+load方法,该方法在main方法之前调用,只调用一次,一般先执行原有类的+load方法再执行分类的+load方法
    • 交换函数指针,以便于调用时走分类扩展的方法
      等等,还有很多常用扩展,根据具体需求来扩展一个不影响原有类的实列方法或类方法。

    Category源码分析

    创建一个Person类的分类,并添加一个属性,一个方法。如下:

    @interface Person (Test)
    @property (nonatomic,strong) NSString *hibo_name;
    -(void)hb_eat;
    @end
    

    终端进入类文件所在位置,通过命令将分类编译成C++形式。如下:

    clang -rewrite-objc Person+Test.m
    

    打开文件找到_category_t如下:

    struct _category_t {
        const char *name;
        struct _class_t *cls;
        const struct _method_list_t *instance_methods;
        const struct _method_list_t *class_methods;
        const struct _protocol_list_t *protocols;
        const struct _prop_list_t *properties;
    };
    

    以结构体的形式存在的,存放方法列表、协议列表、属性列表。

    • name:类名
    • classref_t:类对象,指向所属的类,编译时期没有值,在runtime加载的时候指向对应的类
    • instance_methods:类扩展的实列方法列表
    • class_methods:类扩展的类方法列表
    • protocol_list_t:分类所实现的协议列表
    • _prop_list_t:属性列表

    有相关结构体就一定有对结构体赋值,继续查找如下:

    static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
    {
        "Person",
        0, // &OBJC_CLASS_$_Person,
        (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,
        0,
        0,
        (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test,
    };
    

    这里对上面声明的结构体进行初始化赋值。注意只有方法被实现后才会赋值到结构体属性。

    继续查找发现,下面还定义了_category_t类型的结构体变量。如下:

    static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
        &_OBJC_$_CATEGORY_Person_$_Test,
    };
    

    是一个数组类型的变量,将上面的_category_t对象地址存储在了__DATA数据段中的__objc_catlist中的section段中。

    Category是如何被加载的?

    1、dyld是苹果的动态加载器,用来加载二进制文件;

    2、应用启动时,系统内核先加载dylddyld再将应用中所依赖的各种库加载到内存空间中,而这些工作是在main函数中执行完成的;

    3、_objc_initObject-C runtime的入口函数,读取Mach-O文件OC对应的segment section,并根据其中的数据代码信息,完成为OC的内存布局,以及初始化runtime相关的数据结构。

    通过符号断点探索,下符号断点,点击断点 -> 点击加号,添加_objc_init编译运行:

    在这里插入图片描述
    _dyld_start
    在这里插入图片描述

    1、_objc_init函数

    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // fixme defer initialization until an objc-using image is found?
        environ_init();
        tls_init();
        static_init();
        lock_init();
        exception_init();
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    }
    
    • _dyld_objc_notify_register:会注册三个回调函数
    • map_images:``dyldimage加载到内存时调用
    • load_images:``dyld初始化imageload方法都在此时调用
    • unmap_image:image移除内存时调用

    2、map_images

    map_images(unsigned count, const char * const paths[],
               const struct mach_header * const mhdrs[])
    {
        mutex_locker_t lock(runtimeLock);
        return map_images_nolock(count, paths, mhdrs);
    }
    

    3、map_images_nolock在objc-os.mm中

    map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    {
        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
        firstTime = NO;
    }
    
    • _read_images:读取oc相关的section

    4、_read_images在objc-runtime-new.mm中

    当前方法内开始遍历头文件,看分类中的

    for (EACH_HEADER) {
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();
        for (i = 0; i < count; i++) {
            category_t *cat = catlist[I];
            Class cls = remapClass(cat->cls);
            if (!cls) {
                // Category's target class is missing (probably weak-linked).
                // Disavow any knowledge of this category.
                catlist[i] = nil;
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class", 
                                 cat->name, cat);
                }
                continue;
            }
            // Process this category. 
            // First, register the category with its target class. 
            // Then, rebuild the class's method lists (etc) if 
            // the class is realized. 
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s", 
                                 cls->nameForLogging(), cat->name, 
                                 classExists ? "on existing class" : "");
                }
            }
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)", 
                                 cls->nameForLogging(), cat->name);
                }
            }
        }
    }
    
    • 遍历分类的头文件
    • _getObjc2CategoryList:GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist")
    • addUnattachedCategoryForClass:将当前这个分类加载到这个类当中
    • 注册实例方法到目标类
    • 注册类方法到目标类
    • remethod对原有类的分布重新布局

    5、addUnattachedCategoryForClass在objc-runtime-new.mm中

    static void addUnattachedCategoryForClass(category_t *cat, Class cls, 
                                              header_info *catHeader)
    {
        runtimeLock.assertLocked();
        // DO NOT use cat->cls! cls may be cat->cls->isa instead
        NXMapTable *cats = unattachedCategories();
        category_list *list;
        list = (category_list *)NXMapGet(cats, cls);
        if (!list) {
            list = (category_list *)
                calloc(sizeof(*list) + sizeof(list->list[0]), 1);
        } else {
            list = (category_list *)
                realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
        }
        list->list[list->count++] = (locstamped_category_t){cat, catHeader};
        NXMapInsert(cats, cls, list);
    }
    
    • NXMapInsert(cats, cls, list):将分类和类建立关联

    6、remethodizeClass-重新布局

    static void remethodizeClass(Class cls)
    {
        category_list *cats;
        bool isMeta;
        runtimeLock.assertLocked();
        isMeta = cls->isMetaClass();
        // Re-methodizing: check for more categories
        if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
            if (PrintConnecting) {
                _objc_inform("CLASS: attaching categories to class '%s' %s", 
                             cls->nameForLogging(), isMeta ? "(meta)" : "");
            }
            
            attachCategories(cls, cats, true /*flush caches*/);        
            free(cats);
        }
    }
    

    7、attachCategories

    attachCategories(Class cls, category_list *cats, bool flush_caches)
    {
        if (!cats) return;
        if (PrintReplacedMethods) printReplacements(cls, cats);
        bool isMeta = cls->isMetaClass();
        // fixme rearrange to remove these intermediate allocations
        method_list_t **mlists = (method_list_t **)
            malloc(cats->count * sizeof(*mlists));
        property_list_t **proplists = (property_list_t **)
            malloc(cats->count * sizeof(*proplists));
        protocol_list_t **protolists = (protocol_list_t **)
            malloc(cats->count * sizeof(*protolists));
        // Count backwards through cats to get newest categories first
        int mcount = 0;
        int propcount = 0;
        int protocount = 0;
        int i = cats->count;
        bool fromBundle = NO;
        while (i--) {
            auto& entry = cats->list[I];
            method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
            if (mlist) {
                mlists[mcount++] = mlist;
                fromBundle |= entry.hi->isBundle();
            }
            property_list_t *proplist = 
                entry.cat->propertiesForMeta(isMeta, entry.hi);
            if (proplist) {
                proplists[propcount++] = proplist;
            }
            protocol_list_t *protolist = entry.cat->protocols;
            if (protolist) {
                protolists[protocount++] = protolist;
            }
        }
        auto rw = cls->data();
        prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
        rw->methods.attachLists(mlists, mcount);
        free(mlists);
        if (flush_caches  &&  mcount > 0) flushCaches(cls);
        rw->properties.attachLists(proplists, propcount);
        free(proplists);
        rw->protocols.attachLists(protolists, protocount);
        free(protolists);
    }
    
    • 先声明内存大小
    • method_list_t:二维数组
    • perpareMethodLists:准备方法列表
    • attachLists:添加方法列表、属性列表、协议列表
    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }
    
    • oldCount:获取原有类的方法(或属性、协议)列表大小
    • newCount:加上分类中添加的方法列表大小得到新的大小
    • memmove:拷贝字节,如果目标区域和源区域有重叠的话,能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源内容会被更改。没有重叠和memcpy一样
    • memcpy:拷贝,在发生内存重叠的时候有可能成功有可能失败
    • 将源方法列表或属性协议追加到新的方法列表等上,在获取方法列表新的方法会首先命中

    8、perpareMethodLists

    prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount, 
                       bool baseMethods, bool methodsFromBundle)
    {
        runtimeLock.assertLocked();
        if (addedCount == 0) return;
        // Don't scan redundantly
        bool scanForCustomRR = !cls->hasCustomRR();
        bool scanForCustomAWZ = !cls->hasCustomAWZ();
        // There exist RR/AWZ special cases for some class's base methods. 
        // But this code should never need to scan base methods for RR/AWZ: 
        // default RR/AWZ cannot be set before setInitialized().
        // Therefore we need not handle any special cases here.
        if (baseMethods) {
            assert(!scanForCustomRR  &&  !scanForCustomAWZ);
        }
        // Add method lists to array.
        // Reallocate un-fixed method lists.
        // The new methods are PREPENDED to the method list array.
        for (int i = 0; i < addedCount; i++) {
            method_list_t *mlist = addedLists[I];
            assert(mlist);
            // Fixup selectors if necessary
            if (!mlist->isFixedUp()) {
                fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
            }
            // Scan for method implementations tracked by the class's flags
            if (scanForCustomRR  &&  methodListImplementsRR(mlist)) {
                cls->setHasCustomRR();
                scanForCustomRR = false;
            }
            if (scanForCustomAWZ  &&  methodListImplementsAWZ(mlist)) {
                cls->setHasCustomAWZ();
                scanForCustomAWZ = false;
            }
        }
    }
    
    • fixupMethodList:修复方法列表

    关联对象的探索

    在分类中添加属性,并使用关联对象来实现值的存取。

    //获取关联对象的值
    id objc_getAssociatedObject(id object, const void *key) {
        return _object_get_associative_reference(object, (void *)key);
    }
    //设置关联对象的值
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
        _object_set_associative_reference(object, (void *)key, value, policy);
    }
    //移除关联属性
    void objc_removeAssociatedObjects(id object) 
    {
        if (object && object->hasAssociatedObjects()) {
            _object_remove_assocations(object);
        }
    }
    
    • object:绑定到的对象
    • key:设置绑定key,以便通过key获取绑定值
    • value:绑定值
    • policy:绑定策略
    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                                *   The association is not made atomically. */
        OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                                *   The association is made atomically. */
        OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                                *   The association is made atomically. */
    };
    
    • 设置属性修饰符
    类型 说明
    OBJC_ASSOCIATION_ASSIGN assign
    OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,strong
    OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
    OBJC_ASSOCIATION_RETAIN atomic,strong
    OBJC_ASSOCIATION_COPY atomic,copy

    1、_objc_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;
            //初始化hashMap
            AssociationsHashMap &associations(manager.associations());
            //当前对象的地址按位取反(key)
            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);
                    object->setHasAssociatedObjects();
                }
            } 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);
    }
    
    • acquireValue:根据内存管理语义进行内存管理
    • ObjcAssociation:该对象用来存储关联属性的属性值,两个属性一个存储策略,一个属性值
    • AssociationsManager:管理关ObjcAssociationHashMap对象
    • ObjectAssociationMap:关联对象存储属性值的数据结构,key为用户自定义的字符串,value为属性值

    acquireValue:

    static id acquireValue(id value, uintptr_t policy) {
        switch (policy & 0xFF) {
        case OBJC_ASSOCIATION_SETTER_RETAIN:
            return objc_retain(value);
        case OBJC_ASSOCIATION_SETTER_COPY:
            return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
        }
        return value;
    }
    
    • 根据配置项,对value操作

    AssociationsManager:

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

    AssociationsManager管理着AssociationsHashMap这个类,在该类中提供了对关联对象的属性添加、删除、查找的函数,并且访问是安全的。

    总结如下:

    • AssociationsHashMap:key为对象地址按位取反 valueObjectAssociationMap
    • ObjectAssociationMap:key为用户自定义字符串valueObjcAssociation对象
    • ObjcAssociation:包含两个属性,policy存储策略,value为关联属性的值
    • AssociationsManager -> AssociationsHashMap -> ObjcAssociation -> value

    2、_object_get_associative_reference

    通过对象地址和设置的key获取关联属性值:

    id _object_get_associative_reference(id object, void *key) {
        id value = nil;
        uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            disguised_ptr_t disguised_object = DISGUISE(object);
            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()) {
                    ObjcAssociation &entry = j->second;
                    value = entry.value();
                    policy = entry.policy();
                    if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                        objc_retain(value);
                    }
                }
            }
        }
        if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
            objc_autorelease(value);
        }
        return value;
    }
    

    同存属性值一样,通过mananger来初始化AssociationsHashMap,通过对象地址指针取反作为key来获取ObjectAssociationMap对象,在通过用户自定义key(字符串)在ObjectAssociationMap对象中获取ObjcAssociation对象,该对象存在两个属性,一个为属性值存储策略,另一个为存储的属性值。

    3、_object_remove_assocations

    移除对象的关联对象

    void _object_remove_assocations(id object) {
        vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
        {
            AssociationsManager manager;
            AssociationsHashMap &associations(manager.associations());
            if (associations.size() == 0) return;
            disguised_ptr_t disguised_object = DISGUISE(object);
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // copy all of the associations that need to be removed.
                ObjectAssociationMap *refs = i->second;
                for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                    elements.push_back(j->second);
                }
                // remove the secondary table.
                delete refs;
                associations.erase(i);
            }
        }
        // the calls to releaseValue() happen outside of the lock.
        for_each(elements.begin(), elements.end(), ReleaseValue());
    }
    

    该方法不需要我们手动调用,系统在清理对象的时候会调用,及时清理关联属性。可以在objc-750源码中搜索dealloc来验证一下是否调用过该方法。如下:

    - (void)dealloc {
        _objc_rootDealloc(self);
    }
    * 内部调用了_objc_rootDealloc来清理内存
    _objc_rootDealloc(id obj)
    {
        assert(obj);
        obj->rootDealloc();
    }
    继续追踪:
    objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;  // fixme necessary?
        if (fastpath(isa.nonpointer  &&  
                     !isa.weakly_referenced  &&  
                     !isa.has_assoc  &&  
                     !isa.has_cxx_dtor  &&  
                     !isa.has_sidetable_rc))
        {
            assert(!sidetable_present());
            free(this);
        } 
        else {
            object_dispose((id)this);
        }
    }
    
    • 对isa指针做了判断,判断是否存在一些关联内容,如果都不存在直接释放当前对象,判断是否有弱引用对象、关联对象
    • 如果存在继续调用object_dispose
    object_dispose(id obj)
    {
        if (!obj) return nil;
        objc_destructInstance(obj);    
        free(obj);
        return nil;
    }
    * objc_destructInstance:销毁实例对象
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            bool cxx = obj->hasCxxDtor();
            bool assoc = obj->hasAssociatedObjects();
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            if (assoc) _object_remove_assocations(obj);
            obj->clearDeallocating();
        }
        return obj;
    }
    

    hasAssociatedObjects判断是否存在关联属性,如果有就移除。在这里就调用了_object_remove_assocations方法来移除关联属性。

    相关文章

      网友评论

          本文标题:Category-初探

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