首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

iOS Objective-C 分類為系統類別新增屬性:關聯物件完整實作

     編輯:程式設計疑難解答

問題背景

需為系統類別 (如 UIButton) 新增屬性儲存自訂資料 (如 cityId、typeId),無法繼承 (final 類別) 或不想建立子類別。

解決方案:Category + Associated Objects

.h 檔案:宣告屬性

#import <UIKit/UIKit.h>

@interface UIButton (TLButton)

@property (nonatomic, strong, readwrite) NSNumber *cityId;
@property (nonatomic, strong, readwrite) NSNumber *typeId;
@property (nonatomic, readwrite) NSNumber *progress;

@end

.m 檔案:實作 Getter/Setter 使用關聯物件

#import "UIButton+TLButton.h"
#import <objc/runtime.h>

@implementation UIButton (TLButton)

- (NSNumber *)cityId {
    return objc_getAssociatedObject(self, @selector(cityId));
}

- (void)setCityId:(NSNumber *)cityId {
    [self willChangeValueForKey:@"cityId"];
    objc_setAssociatedObject(self, @selector(cityId), cityId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"cityId"];
}

- (NSNumber *)typeId {
    return objc_getAssociatedObject(self, @selector(typeId));
}

- (void)setTypeId:(NSNumber *)typeId {
    [self willChangeValueForKey:@"typeId"];
    objc_setAssociatedObject(self, @selector(typeId), typeId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"typeId"];
}

- (NSNumber *)progress {
    return objc_getAssociatedObject(self, @selector(progress));
}

- (void)setProgress:(NSNumber *)progress {
    [self willChangeValueForKey:@"progress"];
    objc_setAssociatedObject(self, @selector(progress), progress, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"progress"];
}

@end

關鍵 API 解析

函數用途參數
objc_getAssociatedObject讀取關聯值(id object, const void *key)
objc_setAssociatedObject設定關聯值(id object, const void *key, id value, objc_AssociationPolicy policy)
objc_removeAssociatedObjects移除所有關聯(id object)

關聯策略對照

策略常數記憶體語意等同 @property適用場景
OBJC_ASSOCIATION_ASSIGN弱引用assign / weak委派、非擁有關係
OBJC_ASSOCIATION_RETAIN_NONATOMIC強引用、非原子strong, nonatomic一般物件 (效能較好)
OBJC_ASSOCIATION_COPY_NONATOMIC複製、非原子copy, nonatomicNSString、NSArray 等不可變類別
OBJC_ASSOCIATION_RETAIN強引用、原子strong多執行緒共享
OBJC_ASSOCIATION_COPY複製、原子copy多執行緒共享不可變物件

使用範例

UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.cityId = @1001;
btn.typeId = @2;
btn.progress = @0.5;

NSLog(@"City: %@, Type: %@, Progress: %@", btn.cityId, btn.typeId, btn.progress);

Category vs 繼承 比較

維度Category (分類)繼承 (子類別)
適用性系統類別、無法修改源碼自有類別、需擴展行為
屬性儲存需用 Associated Objects直接新增實例變數
方法覆寫可覆寫但危險 (方法交換)安全覆寫、呼叫 super
編譯檢查較弱完整型別檢查
推薦場景為現有類別「貼上」功能建立專用型別、IS-A 關係

最佳實務建議

  • Key 使用 @selector(propertyName) 確保唯一性且易除錯
  • 實作 willChangeValueForKey/didChangeValueForKey 支援 KVO
  • 避免在 Category 覆寫系統方法 (用 Method Swizzling 並謹慎)
  • Swift 中用 objc_setAssociatedObject 同樣適用,或用 Extension + 儲存至外部字典
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved