Category(分類) 是 Objective-C 獨有的語言特性,允許在不繼承、不修改原始碼的前提下,為既有類別(含系統框架類別如 NSString、NSArray)動態新增方法。它實現了「開放-封閉原則」:對擴充開放,對修改封閉。
| 維度 | Category | 繼承 |
|---|---|---|
| 能否新增實例變數(Ivar) | ❌ 無法(僅能用關聯物件模擬) | ✅ 可自由新增 |
| 能否覆寫既有方法 | ⚠️ 可行但極度不建議(會遮蔽原實作,且無法呼叫 super) | ✅ 正規覆寫,可呼叫 super |
| 編譯單元分離 | ✅ 同一類別可拆分多個 .h/.m,利於團隊協作 | ❌ 子類別通常集中在單一檔案 |
| 命名空間隔離 | ✅ 可用 Private 分類實現「檔案級私有方法」 | ❌ 子類別方法皆公開 |
// 擴充 NSString 加入字串反轉功能
// 檔名:NSString+ReverseString.h / .m
// NSString+ReverseString.h
#import
@interface NSString (ReverseString)
- (NSString *)reversedString;
@end
// NSString+ReverseString.m
#import "NSString+ReverseString.h"
@implementation NSString (ReverseString)
- (NSString *)reversedString {
NSUInteger len = self.length;
unichar *buffer = malloc(len * sizeof(unichar));
[self getCharacters:buffer range:NSMakeRange(0, len)];
for (NSUInteger i = 0; i < len / 2; i++) {
unichar tmp = buffer[i];
buffer[i] = buffer[len - 1 - i];
buffer[len - 1 - i] = tmp;
}
NSString *result = [NSString stringWithCharacters:buffer length:len];
free(buffer);
return result;
}
@end
將分類宣告與實作直接寫在 .m 檔案頂部,外部不可見:
// Foo.m
@interface Foo (Private)
- (void)internalHelper;
@end
@implementation Foo (Private)
- (void)internalHelper { NSLog(@"內部輔助方法"); }
@end
@implementation Foo
- (void)publicMethod { [self internalHelper]; }
@end
雖然 Category 不能加 Ivar,但可用 objc_setAssociatedObject 綁定額外資料:
// UIView+TagExtension.m
#import
static const char kAssociatedTagKey;
@implementation UIView (TagExtension)
- (void)setExtendedTag:(id)tag {
objc_setAssociatedObject(self, &kAssociatedTagKey, tag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)extendedTag {
return objc_getAssociatedObject(self, &kAssociatedTagKey);
}
@end
xyz_reversedString。FOUNDATION_EXPORT 或在模組映射中公開。Category 是 Objective-C 實現「組合優於繼承」的利器。善用它可:
NSString 加密、UIColor 十六進位初始化)。ViewController+Networking、ViewController+UILayout)。Private 分類替代 C++ private 關鍵字,實現編譯期可見性控制。記住核心限制:只能加方法,不能加 Ivar;若需狀態,請改用關聯物件或繼承。