在 Objective-C 中,Category(類別) 是一項強大的語言特性,允許開發者在不修改原始類別原始碼、也不需要建立子類別的情況下,為現有類別新增方法。這對於擴展系統框架類別(如 `NSString`、`NSArray`、`UIView` 等)或是將龐大類別的功能模組化特別有用。
例如為 `NSArray` 新增「反轉陣列」或「轉 JSON」方法,所有 `NSArray` 及其子類別(如 `NSMutableArray`)自動獲得新功能。
當只需增加少數輔助方法時,建立 Category 比建立子類別更輕量,且不需修改既有物件建立邏輯。
將龐大類別的功能拆分為多個 Category(如 `UIView+Layout`、`UIView+Animation`),便於團隊協作與程式碼管理。
檔案命名:`原類別名+功能描述`,例如 `NSString+Revert.h/m`。
介面宣告:
@interface NSString (Revert) - (NSString *)reversedString; @end
實作區塊:
@implementation NSString (Revert)
- (NSString *)reversedString {
// 實作邏輯
}
@end
#import@interface Person : NSObject @property (nonatomic, copy) NSString *name; - (void)test; - (void)eat; @end // Category: Creation — 提供便利建構器 @interface Person (Creation) - (instancetype)initWithName:(NSString *)aName; @end // Category: Life — 新增生活行為 @interface Person (Life) - (void)eat; - (void)sleep; - (void)play; @end
#import "Person.h"
@implementation Person
- (void)test { NSLog(@"這是原有類別的方法"); }
- (void)eat { NSLog(@"這是原有類別的 eat"); }
@end
@implementation Person (Creation)
- (instancetype)initWithName:(NSString *)aName {
self = [super init];
if (self) self.name = aName;
return self;
}
@end
@implementation Person (Life)
// 注意:此處覆蓋了原類別的 eat 方法
- (void)eat { NSLog(@"他需要吃飯"); }
- (void)sleep { NSLog(@"他需要睡覺"); }
- (void)play { NSLog(@"他需要玩耍"); }
@end
#import#import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *jack = [[Person alloc] initWithName:@"Jack"]; NSLog(@"Person: %@", jack.name); [jack test]; // 原類別方法 [jack eat]; // Category 覆蓋版本 [jack sleep]; // Category 新增方法 [jack play]; // Category 新增方法 } return 0; }
#import@interface NSArray (Convert) + (NSMutableArray *)arrayFromNumber:(int)number; @end
#import "NSArray+Convert.h"
@implementation NSArray (Convert)
+ (NSMutableArray *)arrayFromNumber:(int)number {
NSMutableArray *numberArray = [NSMutableArray array];
while (number) {
int last = number % 10;
number /= 10;
[numberArray addObject:@(last)];
}
return numberArray;
}
@end
#import "NSArray+Convert.h" NSMutableArray *arr = [NSArray arrayFromNumber:123]; // arr => @[@3, @2, @1] (個位在前) for (NSNumber *n in arr) NSLog(@"number: %@", n);
| 限制 | 說明 | 替代方案 |
|---|---|---|
| 無法新增實例變數 | Category 沒有儲存空間 | 使用 Associated Objects (`objc_setAssociatedObject`) 或改用子類別 |
| 方法覆蓋導致 super 斷裂 | Category 方法優先級較高,無法呼叫原實作 | 避免覆蓋現有方法;若必須修改行為,請用 Method Swizzling 或子類別 |
| 無法宣告屬性(自動生成存取方法) | 屬性需有實例變數支援 | 手動實作 getter/setter 並搭配 Associated Objects |
Foundation 框架大量使用 Category 組織功能,例如 `NSArray` 擁有 `NSArray+Deprecated`、`NSArray+KeyValueCoding` 等內部分類。這證明 Category 是大型專案模組化的成熟方案。
Category 是 Objective-C 實現「開放封閉原則」的利器:對擴展開放,對修改關閉。善用 Category 可讓程式碼更清晰、易維護,但務必遵守「不覆蓋原方法」、「不存狀態」兩大鐵律,避免引入難以除錯的問題。