利用 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 擴展一個方法。dom
NSArray+Random.hui
#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.mcode
#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 編程的靈活性,但也不能濫用,會致使程序很差理解。對象