Category-初探

参考: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 > 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-初探)