在既有類中使用關(guān)聯(lián)對象存放自定義數(shù)據(jù)
通俗講就是給一個(gè)對象關(guān)聯(lián)許多其他對象牌里,這些對象通過“鍵”來區(qū)分颊咬。當(dāng)然這里可以指明“存儲(chǔ)策略”,下面再說牡辽。
先來看看管理關(guān)聯(lián)對象的幾個(gè)方法:
1喳篇、以給定的key和存儲(chǔ)策略為某對象設(shè)置關(guān)聯(lián)對象值
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
分析各個(gè)參數(shù)含義:
1、id object:被關(guān)聯(lián)的對象
2态辛、const void *key: 這里分為兩部分分析麸澜,const 和 void *
const:定義常量
void *:無類型指針,可以指向任何類型的數(shù)據(jù)
const void *zsz:這里定義了一個(gè)指針zsz奏黑,指針zsz可以指向任何類型的值痰憎,但這個(gè)值一定是常量。我們不能通過這個(gè)指針改變這個(gè)常量的值攀涵,但可以改變這個(gè)指針指向不同的保存著常量的內(nèi)存空間
// 舉例
*zsz = 2017; // 錯(cuò)誤铣耘,不能改變這塊內(nèi)存空間的值,應(yīng)該定義為常量
const a = 123;
const b = 456;
zsz = &a; // 正確以故,zsz指針指向常量a的地址
zsz = &b;
3蜗细、id value:關(guān)聯(lián)的對象,就是這個(gè)對象本來沒有但是我們想要給他加的東西怒详,如本例子中的arrayName
4炉媒、objc_AssociationPolicy policy:內(nèi)存管理策略
objc_AssociationPolicy:一個(gè)枚舉類型的數(shù)據(jù)結(jié)構(gòu),假如關(guān)聯(lián)對象成為了屬性,那么它就會(huì)具備相應(yīng)的語義
enum {
OBJC_ASSOCIATION_ASSIGN = 0, // 給關(guān)聯(lián)對象指定弱引用,相當(dāng)于@property(assign)或@property(unsafe_unretained)
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 給關(guān)聯(lián)對象指定非原子的強(qiáng)引用,相當(dāng)于@property(nonatomic,strong)或@property(nonatomic,retain)
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // 給關(guān)聯(lián)對象指定非原子的copy特性,相當(dāng)于@property(nonatomic,copy)
OBJC_ASSOCIATION_RETAIN = 01401, // 給關(guān)聯(lián)對象指定原子強(qiáng)引用,相當(dāng)于@property(atomic,strong)或@property(atomic,retain)
OBJC_ASSOCIATION_COPY = 01403 // 給關(guān)聯(lián)對象指定原子copy特性,相當(dāng)于@property(atomic,copy)
};
typedef uintptr_t objc_AssociationPolicy;
關(guān)聯(lián)類型 等效的@property
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC nonatiomic,copy
OBJC_ASSOCIATION_RETAIN retain
OBJC_ASSOCIATION_COPY copy
2昆烁、根據(jù)給定的鍵從某對象中獲取相應(yīng)的關(guān)聯(lián)對象值
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
3吊骤、移除指定對象的全部關(guān)聯(lián)對象
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0);
舉個(gè)例子
給數(shù)組添加一個(gè)分類,分類中關(guān)聯(lián)一個(gè)字符串類型的對象來表示這個(gè)數(shù)組的名字静尼,代碼如下:
// NSArray+ZSZName.h
#import <Foundation/Foundation.h>
@interface NSArray (ZSZName)
@property (nonatomic, strong) NSString *arrayName;
@end
// NSArray+ZSZName.m
#import "NSArray+ZSZName.h"
#import <objc/runtime.h>
@implementation NSArray (ZSZName)
// 首先先用@dynamic 修飾屬性白粉,這樣編譯器不會(huì)自動(dòng)實(shí)現(xiàn)setter和getter方法
@dynamic arrayName;
- (void)setArrayName:(NSString *)arrayName {
/*
@selector() :這個(gè)作為方法的第二個(gè)參數(shù),第二個(gè)參數(shù)一般定義一個(gè)靜態(tài)全局變量鼠渺,以保證是唯一的鸭巴,這里使用@selector(方法名),返回的是SEL類型拦盹,SEL方法編號械荷,可以通過 Dispatch table尋找到對應(yīng)的IMP(IMP:函數(shù)指針)
*/
objc_setAssociatedObject(self, @selector(arrayName), arrayName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)arrayName {
return objc_getAssociatedObject(self, @selector(arrayName));
}
@end
控制器中使用
#import "ViewController.h"
#import "NSArray+ZSZName.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSArray *schoolArray = [[NSArray alloc] initWithObjects:@"附城中學(xué)",@"城中小學(xué)",@"彭湃中學(xué)", nil];
[schoolArray setArrayName:@"學(xué)校"];
NSLog(@"數(shù)組的名字:%@",schoolArray.arrayName);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
要點(diǎn):
1瀑构、可以通過“關(guān)聯(lián)對象”機(jī)制來把兩個(gè)對象連起來稍刀;
2、定義關(guān)聯(lián)對象時(shí)可指定內(nèi)存管理語義校读,用以模仿定義屬性時(shí)所采用的“擁有關(guān)系”和“非擁有關(guān)系”;
3祖能、只有在其他做法不可行時(shí)才應(yīng)選用關(guān)聯(lián)對象地熄,因?yàn)檫@種做法通常會(huì)引入難以查找的bug。