首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C Associated Objects 關聯對象:原理、用法、內存管理策略與避坑指南

     編輯:關於C語言

Objective-C Associated Objects 關聯對象:原理、用法、內存管理策略與避坑指南

關聯對象允許在無法修改類定義(如系統類、第三方庫類)時,向現有對象動態附加自定義數據。本文深度解析API、內存管理語義、典型應用場景(如Block回調綁定)、循環引用風險與最佳實踐。

一、核心API

// 設置關聯
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_ASSIGNassign / unsafe_unretained弱引用,不保留,對象釋放後指針成懸空指針
OBJC_ASSOCIATION_RETAIN_NONATOMICnonatomic, strong強引用,非原子操作
OBJC_ASSOCIATION_COPY_NONATOMICnonatomic, copy複製,非原子操作
OBJC_ASSOCIATION_RETAINatomic, strong強引用,原子操作(線程安全)
OBJC_ASSOCIATION_COPYatomic, copy複製,原子操作

推薦:多數場景用OBJC_ASSOCIATION_RETAIN_NONATOMIC(對象)或OBJC_ASSOCIATION_COPY_NONATOMIC(Block/不可變對象)。

三、經典應用:為UIAlertView綁定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。

六、生命周期管理

  • 關聯對象隨宿主對象釋放自動釋放(Runtime在dealloc中清理)。
  • 手動移除:objc_setAssociatedObject(obj, key, nil, policy) 或 objc_removeAssociatedObjects(obj)(清空所有關聯,慎用)。

七、參考資源

  • Apple官方文檔:Objective-C Runtime Programming Guide → Associated Objects
  • 深度好文:Associated Objects Tutorial(英文,淺顯易懂)

關聯對象是Runtime動態性的集中體現,善用它可實現「不修改源碼擴展類能力」、解耦委托回調、實現分類屬性,是中高階iOS開發必備技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved