Objective-C 中关联引用的概念

关联引用概念

利用 OC 语言的动态性,借助运行时(runtime)的功能,我们可以为已存在的实例对象增加实例变量,这个功能叫做关联引用。

添加、检索和断开关联

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,id _Nullable value, objc_AssociationPolicy policy)

该方法为对象 object 添加以 key 指定的地址作为关键字、以value为值的关联引用,第四个参数policy指定关联引用的存储策略。

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key) OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

返回 object 以 Key 为关键字的关联对象,如果没有关联对象,则返回 nil

objc_removeAssociatedObjects(id _Nonnull object)
    OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);

断开关联

存储策略

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /*弱引用对象保存对象*/
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /*强引用对象保存对象,非原子性*/
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /*复制一份原对象,非原子性*/
    OBJC_ASSOCIATION_RETAIN = 01401,       /*引用对象保存对象,默认原子性,多线程安全 */
    OBJC_ASSOCIATION_COPY = 01403          /*复制一份原对象,默认原子性,多线程安全*/
};

实例

假设我们为 NSArray 增加了一个新的随机取元素的方法,并且取得的元素不可以连续相同,我们利用范畴(category)为 NSArray 扩展一个方法。

NSArray+Random.h

#import <Foundation/Foundation.h>

@interface NSArray (Random)

- (id)anyOne;

@end

NSArray+Random.m

#import "NSArray+Random.h"
#import <objc/runtime.h>

@implementation NSArray (Random)

static char prevKey;

- (id)anyOne {
    id item;
    NSUInteger count = [self count];
    if (count == 0) {
        return nil;
    }else if(count == 1){
        return [self lastObject];
    }else{
        id prev = objc_getAssociatedObject(self, &prevKey);//获取关联对象所引用的值,初次使用返回 nil
        NSUInteger index = random()%count;
        item = self[index];
        if (item == prev) {//索引相同情况下,取下一个元素,若该索引是数组最后一个,则取第一个值
            if (++index >= count) {
                index = 0;
            }
            item = self[index];
        }
        printf("item:%s,prevItem:%s\n",[item UTF8String],[prev UTF8String]);
    }
    
    objc_setAssociatedObject(self, &prevKey, item, OBJC_ASSOCIATION_RETAIN);//存储最后返回的对象
    return item;
}

main.m

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "NSArray+Random.h"

int main(int argc, char * argv[]) {
    
    
    id arr1 = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7"];
    id arr2 = @[@"a",@"b",@"c",@"d",@"e",@"f",@"g"];
    
    for (int i=0; i<15; i++) {
        printf("arr1:%s,arr2:%s\n",[[arr1 anyOne] UTF8String],
               [[arr2 anyOne] UTF8String]);
    }
    
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

运行结果

item:2,prevItem:(null)
item:e,prevItem:(null)
arr1:2,arr2:e
item:3,prevItem:2
item:f,prevItem:e
arr1:3,arr2:f
item:2,prevItem:3
item:d,prevItem:f
arr1:2,arr2:d
item:4,prevItem:2
item:c,prevItem:d
arr1:4,arr2:c
item:2,prevItem:4
item:d,prevItem:c
arr1:2,arr2:d
item:3,prevItem:2
item:f,prevItem:d
arr1:3,arr2:f
item:7,prevItem:3
item:e,prevItem:f
arr1:7,arr2:e
item:1,prevItem:7
item:a,prevItem:e
arr1:1,arr2:a

结语

综合使用关联引用和范畴,可以大大增强 OC 编程的灵活性,但也不能滥用,会导致程序不好理解。

相关推荐