Objective-C 類擴展機制全解析:Category、Extension、Protocol 與 Delegate 模式
Objective-C 通過 Category(分類)、Extension(類擴展)、Protocol(協議)三大機制實現類的功能擴展、私有接口管理與解耦通信。本文深度對比三者異同,並以經典「婚禮委託」示例完整演示 Protocol + Delegate 實戰模式。
一、Category(分類):為現有類動態添加方法
核心特點
- 無需源碼、無需繼承,即可為任意類(包括系統類)添加實例方法/類方法。
- 添加的方法成為原類一部分,子類可繼承,區別於繼承只能擴展子類。
- 不能添加實例變量(ivar),但可通過關聯對象
objc_setAssociatedObject 實現類似效果。
- 命名規範:
ClassName+CategoryName.h/.m。
聲明與實現
// NSString+Reverse.h
@interface NSString (Reverse)
- (NSString *)reversedString;
@end
// NSString+Reverse.m
#import "NSString+Reverse.h"
@implementation NSString (Reverse)
- (NSString *)reversedString {
NSMutableString *reversed = [NSMutableString stringWithCapacity:self.length];
for (NSUInteger i = self.length; i > 0; i--) {
[reversed appendFormat:@"%C", [self characterAtIndex:i-1]];
}
return reversed;
}
@end
// 使用
NSString *s = @"hello";
NSLog(@"%@", [s reversedString]); // "olleh"
Category vs Subclass 對比
| 維度 | Category | Subclass |
| 添加方法 | ✅ | ✅ |
| 添加實例變量 | ❌ (需關聯對象) | ✅ |
| 方法作用域 | 原類及所有子類 | 僅子類及其子類 |
| 調用方式 | 原類實例/類直接調用 | 需創建子類實例 |
| 適用場景 | 系統類擴展、模塊化功能切片 | 需存儲狀態、重寫父類行為 |
二、Extension(類擴展):私有接口的歸宿
核心特點
- 只能用於擁有源碼的類,定義在
.m 文件頂部(類似私有頭文件)。
- 用於聲明私有方法、私有屬性、協議遵循,隱藏實現細節。
- 編譯器強制要求在
@implementation 中實現所有聲明的方法,否則警告。
- 可添加實例變量(現代 Runtime 支持),
@interface ClassName () { int _privateIvar; } @end。
語法示例
// MyClass.m
#import "MyClass.h"
@interface MyClass () // 無名分類 = Extension
@property (nonatomic, strong) NSString *privateCache; // 私有屬性
- (void)privateHelperMethod; // 私有方法
@end
@implementation MyClass
- (void)privateHelperMethod {
// 實現細節對外不可見
}
@end
Category vs Extension
| 維度 | Category | Extension |
| 作用 | 為無源碼類添加功能 | 管理本類私有接口 |
| 文件結構 | 獨立 .h/.m | 寫在 .m 文件內部 |
| 添加 ivar | 需關聯對象 | 直接在大括號中聲明 |
| 編譯器檢查 | 不強制實現 | 強制在 @implementation 中實現 |
三、Protocol(協議)與 Delegate(委託)模式
協議本質
- 定義一組方法簽名(僅聲明),類似 Java Interface / C++ 純虛類。
- 方法默認
@required(必須實現),可用 @optional 標記可選方法。
- 協議可繼承其他協議
@protocol MyProto 。
Delegate 六步走(以「結婚」為例)
- 聲明協議(女方定義標準):
@protocol Marriage - (void)makeMoney; @optional - (void)cook; @end
- 設置代理屬性(女方持有代理):
@property (nonatomic, weak) id delegate; 必須用 weak 避免循環引用。
- 觸發代理回調(女方通知):
[self.delegate makeMoney];
- 遵循協議(男方簽署):
@interface Groom : NSObject
- 指定代理(女方指定男方):
bride.delegate = groom;
- 實現協議方法(男方履行):
- (void)makeMoney { NSLog(@"我賺錢養家"); }
完整代碼示例
// Bride.h
@protocol Marriage
@required - (void)makeMoney;
@optional - (void)cook; - (void)washClothes;
@end
@interface Bride : NSObject
@property (nonatomic, weak) id delegate;
@property (nonatomic, copy) NSString *name;
- (void)marry;
@end
// Bride.m
@implementation Bride
- (void)marry {
NSLog(@"%@ 和 %@ 結婚了", self.name, self.delegate);
[self.delegate makeMoney]; // 必須實現
if ([self.delegate respondsToSelector:@selector(cook)]) {
[self.delegate cook]; // 可選實現
}
}
@end
// Groom.h
@interface Groom : NSObject
@property (nonatomic, copy) NSString *name;
@end
// Groom.m
@implementation Groom
- (void)makeMoney { NSLog(@"%@ 負責掙錢", self.name); }
- (void)cook { NSLog(@"%@ 做飯", self.name); }
@end
// main.m
int main() {
Bride *bride = [[Bride alloc] init]; bride.name = @"Mary";
Groom *groom = [[Groom alloc] init]; groom.name = @"John";
bride.delegate = groom; // 步驟5
[bride marry]; // 觸發委託
return 0;
}
Delegate 核心優勢
- 解耦:Bride 不關心 Groom 具體類,只關心是否遵循 Marriage 協議。
- 單向依賴:Bride 依賴協議,Groom 依賴協議,雙方不直接依賴彼此具體類。
- 可替換性:可隨時換成 Bride 的其他代理(如律師、父母),無需修改 Bride 代碼。
總結
- Category:橫向擴展現有類功能,無狀態擴展首選。
- Extension:縱向封裝私有實現,.m 文件內部管理。
- Protocol + Delegate:縱向解耦協作關係,代理屬性必須 weak,可選方法用
respondsToSelector: 保護調用。
- 三者協同構建了 Objective-C 靈活、模塊化、可測試的架構基石。