關聯對象允許在無法修改類定義(如系統類、第三方庫類)時,向現有對象動態附加自定義數據。本文深度解析API、內存管理語義、典型應用場景(如Block回調綁定)、循環引用風險與最佳實踐。
// 設置關聯
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
// 獲取關聯
id objc_getAssociatedObject(id object, const void *key);
// 移除所有關聯
void objc_removeAssociatedObjects(id object);
key:唯一標識關聯的指針,常用static char key或@selector(xxx)地址。policy:決定value的保留語義,對應@property屬性。| 策略常量 | 等效@property屬性 | 語義 |
|---|---|---|
OBJC_ASSOCIATION_ASSIGN | assign / unsafe_unretained | 弱引用,不保留,對象釋放後指針成懸空指針 |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | nonatomic, strong | 強引用,非原子操作 |
OBJC_ASSOCIATION_COPY_NONATOMIC | nonatomic, copy | 複製,非原子操作 |
OBJC_ASSOCIATION_RETAIN | atomic, strong | 強引用,原子操作(線程安全) |
OBJC_ASSOCIATION_COPY | atomic, copy | 複製,原子操作 |
推薦:多數場景用OBJC_ASSOCIATION_RETAIN_NONATOMIC(對象)或OBJC_ASSOCIATION_COPY_NONATOMIC(Block/不可變對象)。
系統UIAlertView委托模式分散代碼,用關聯對象將Block直接綁定到Alert實例:
- (void)showAlertWithBlock:(void(^)(NSInteger buttonIndex))handler {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"
message:@"確定操作?"
delegate:self
cancelButtonTitle:@"取消"
otherButtonTitles:@"確定", nil];
// 關聯Block(copy語義,防止Block在棧上銷毀)
static char blockKey;
objc_setAssociatedObject(alert, &blockKey, handler, OBJC_ASSOCIATION_COPY_NONATOMIC);
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
static char blockKey;
void (^handler)(NSInteger) = objc_getAssociatedObject(alertView, &blockKey);
if (handler) handler(buttonIndex);
}
優勢:回調邏輯與觸發代碼局部聚合,無需實現委托協議、標記tag、維護字典映射。
在分類中聲明@property,在getter/setter中轉發到關聯對象:
// NSObject+Associated.h
@interface NSObject (Associated)
@property (nonatomic, strong) id customTag;
@end
// NSObject+Associated.m
#import
static char tagKey;
@implementation NSObject (Associated)
- (id)customTag {
return objc_getAssociatedObject(self, &tagKey);
}
- (void)setCustomTag:(id)tag {
objc_setAssociatedObject(self, &tagKey, tag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
// 使用
view.customTag = @{@"userId": @123};
NSLog(@"%@", view.customTag);
實現了為任意對象動態添加屬性,極大擴展系統類能力。
場景:Block捕獲self,self又通過關聯對象強引用Block → 循環引用。
// 錯誤示例
objc_setAssociatedObject(self, &key, ^{ [self doSomething]; }, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 正確:Block內弱引用self
__weak typeof(self) weakSelf = self;
objc_setAssociatedObject(self, &key, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) [strongSelf doSomething];
}, OBJC_ASSOCIATION_COPY_NONATOMIC); // Block用copy
原則:Block用copy,捕獲self用weak。
dealloc中清理)。objc_setAssociatedObject(obj, key, nil, policy) 或 objc_removeAssociatedObjects(obj)(清空所有關聯,慎用)。Objective-C Runtime Programming Guide → Associated Objects關聯對象是Runtime動態性的集中體現,善用它可實現「不修改源碼擴展類能力」、解耦委托回調、實現分類屬性,是中高階iOS開發必備技能。