Category(分類)允許在**不繼承、不修改原始碼**的情況下,為現有類別**新增方法**。編譯器將分類方法合併至類別的方法列表,運行時無差別調用。主要用途:
NSString+Utils)。利用 **Class Extension (匿名分類)** 在 .m 檔重新宣告屬性為 readwrite,對外僅暴露 readonly。
@interface Things : NSObject
@property (assign) NSInteger thing1;
@property (readonly, assign) NSInteger thing2; // 唯讀
- (void)resetAllValue;
@end
@interface Things () // Class Extension
@property (assign) NSInteger thing3; // 完全私有
@property (readwrite, assign) NSInteger thing2; // 內部可寫
@end
@implementation Things
@synthesize thing1, thing2, thing3;
- (void)resetAllValue {
self.thing1 = 1;
self.thing2 = 2; // 內部可寫
self.thing3 = 3;
}
- (NSString *)description {
return [NSString stringWithFormat:@"thing1=%ld, thing2=%ld, thing3=%ld",
self.thing1, self.thing2, self.thing3];
}
@end
將單一巨型類別的方法按功能領域拆分到多個 CategoryName.m,編譯時自動合併。
@interface CategoryThing : NSObject {
NSInteger thing1, thing2, thing3;
}
@end
// 分類宣告(僅方法,無實例變數)
@interface CategoryThing (Thing1)
- (void)setThing1:(NSInteger)v;
- (NSInteger)thing1;
@end
@interface CategoryThing (Thing2)
- (void)setThing2:(NSInteger)v;
- (NSInteger)thing2;
@end
@interface CategoryThing (Thing3)
- (void)setThing3:(NSInteger)v;
- (NSInteger)thing3;
@end
@implementation CategoryThing (Thing1)
- (void)setThing1:(NSInteger)v { thing1 = v; }
- (NSInteger)thing1 { return thing1; }
@end
Thing2.m、Thing3.m 同理。注意:分類**不能新增實例變數**,上例中 thing1/2/3 必須在主介面 @interface CategoryThing : NSObject { ... } 宣告。
為 NSString 新增 lengthAsNumber 方法,回傳長度的 NSNumber。
@interface NSString (NumberConversion)
- (NSNumber *)lengthAsNumber;
@end
@implementation NSString (NumberConversion)
- (NSNumber *)lengthAsNumber {
NSUInteger len = [self length];
return [NSNumber numberWithUnsignedInteger:len];
}
@end
#import "NSString+NumberConversion.h"
NSNumber *n = @"Hello".lengthAsNumber; // n.integerValue == 5
| 主題 | 說明 |
|---|---|
| 關聯物件 | 分類無法加 ivar,但可用 objc_setAssociatedObject/objc_getAssociatedObject 綁定任意物件,實現「分類擁有狀態」。 |
| 方法覆蓋風險 | 若多個分類實作同名方法,**編譯順序決定勝負**,行為不可預測。**嚴禁**在分類中覆蓋主類別或其他分類的方法。 |
| 載入時機 | 分類在 dyld 載入映像時自動註冊,+load 會被調用,適合執行初始化邏輯。 |
| 命名慣例 | 檔名 ClassName+Feature.h/m,分類名 (Feature),方法前綴加獨特標識(如 xyz_)避免衝突。 |
| Swift 互操作 | Swift 無法直接使用 Objective-C 分類方法,需暴露為 extension 或封裝為獨立函數。 |
@interface UIView (TagObject)
@property (nonatomic, strong) id tagObject;
@end
@implementation UIView (TagObject)
static const void *TagKey = &TagKey;
- (void)setTagObject:(id)obj {
objc_setAssociatedObject(self, TagKey, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)tagObject {
return objc_getAssociatedObject(self, TagKey);
}
@end
Category 是 Objective-C 動態特性的精華,善用它能讓程式碼結構清晰、擴充彈性、團隊協作高效。牢記「只加方法、不加 ivar、不覆蓋」三原則,即可安享其利。