首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

Objective-C Category 類別機制深度解析與實戰應用

     編輯:關於C

什麼是 Category(類別)?

在 Objective-C 中,Category(類別) 是一項強大的語言特性,允許開發者在不修改原始類別原始碼、也不需要建立子類別的情況下,為現有類別新增方法。這對於擴展系統框架類別(如 `NSString`、`NSArray`、`UIView` 等)或是將龐大類別的功能模組化特別有用。

Category 的核心特性

  1. 方法合併:Category 中定義的方法會在執行時合併到原始類別中,呼叫方式與原生方法完全相同。
  2. 繼承傳遞:父類別的 Category 方法會被子類別繼承;但子類別的 Category 方法不會回傳給父類別。
  3. 無法新增實例變數:Category 無法儲存狀態(無法加入 ivar),若需新增屬性請改用子類別或 Associated Objects。
  4. 方法覆蓋風險:若 Category 定義了與原類別同名的方法,Category 版本會取代原版本,且無法透過 `super` 呼叫原實作,容易造成行為異常。

Category 的三大應用場景

1. 擴展系統框架類別

例如為 `NSArray` 新增「反轉陣列」或「轉 JSON」方法,所有 `NSArray` 及其子類別(如 `NSMutableArray`)自動獲得新功能。

2. 替代輕量級子類別

當只需增加少數輔助方法時,建立 Category 比建立子類別更輕量,且不需修改既有物件建立邏輯。

3. 程式碼模組化與維護

將龐大類別的功能拆分為多個 Category(如 `UIView+Layout`、`UIView+Animation`),便於團隊協作與程式碼管理。

命名慣例與宣告語法

檔案命名:`原類別名+功能描述`,例如 `NSString+Revert.h/m`。
介面宣告:

@interface NSString (Revert)
- (NSString *)reversedString;
@end

實作區塊:

@implementation NSString (Revert)
- (NSString *)reversedString {
    // 實作邏輯
}
@end

實戰範例:擴展自訂 Person 類別

Person.h — 原始類別與兩個 Category

#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

Person.m — 實作與方法覆蓋示範

#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

main.m — 測試呼叫

#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;
}

實戰範例:擴展系統類別 NSArray

NSArray+Convert.h

#import 

@interface NSArray (Convert)
+ (NSMutableArray *)arrayFromNumber:(int)number;
@end

NSArray+Convert.m

#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 的限制與最佳實踐

限制說明替代方案
無法新增實例變數Category 沒有儲存空間使用 Associated Objects (`objc_setAssociatedObject`) 或改用子類別
方法覆蓋導致 super 斷裂Category 方法優先級較高,無法呼叫原實作避免覆蓋現有方法;若必須修改行為,請用 Method Swizzling 或子類別
無法宣告屬性(自動生成存取方法)屬性需有實例變數支援手動實作 getter/setter 並搭配 Associated Objects

Apple 官方框架中的 Category 應用

Foundation 框架大量使用 Category 組織功能,例如 `NSArray` 擁有 `NSArray+Deprecated`、`NSArray+KeyValueCoding` 等內部分類。這證明 Category 是大型專案模組化的成熟方案。

總結

Category 是 Objective-C 實現「開放封閉原則」的利器:對擴展開放,對修改關閉。善用 Category 可讓程式碼更清晰、易維護,但務必遵守「不覆蓋原方法」、「不存狀態」兩大鐵律,避免引入難以除錯的問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved