# Category in Objective-C

> 

Published: 2010-12-16
URL: https://kaochenlong.com/category-in-objective-c

---

OOP 的精神之一，就是如果你想研發一台新款式的車子，你並不需要重新發明輪子，通常的做法會去繼承某個現有的”車子類別”，然後加上你要的功能跟屬性，改一改就變成一款新的車子可以來騙錢了。

不過有時候你想幫原來的類別加功能，但又不想動到原來的程式碼，例如你可能下載了某款功能超強的 2D 物理引擎程式碼，但因為某些小地方寫的不合你的需求，於是你便動手改原始碼來加功能。這當然沒問題，但萬一原作出新的版本，你要不就選擇維持自己原來的版本不 update，不然就是 update 之後，你原來加在舊版本的程式碼得再重貼一次到新版。

&lt;!-- more --&gt;

Objective-C 裡有個叫做 `category` 的東西可以幫你在現有的類別加上新功能，這樣一來上面這個問題就可以搞定了。跟別的程式語言比較起來，category 的觀念有點像是在 Ruby 的 mixin 或是 Python 的 open class，都是在不影響或修改原來的類別或模組的情況下去修改原有的功能。

舉個例子，因為 NSObject 是所有物件的源頭，但我想要加一個方法讓所有的子類別都可用(把要加的功能放在繼承階層的最源頭並不是好的設計，在這裡只是舉個例子而已)。程式碼這樣寫：

```objc
// interface
@interface NSObject(MySuperObject)
-(void) printRetainCount;
+(void) sayHello;
@end

// implementation
@implementation NSObject(MySuperObject)

-(void) printRetainCount {
	NSLog(@&quot;The retain count is %d&quot;, [self retainCount]);
}

+(void) sayHello {
	NSLog(@&quot;Hello everybody!&quot;);
}

@end
```

這裡用的是在後面加個小括號以及 category 的名稱。要注意的是 category 只能加 method（instance method 或是 class method 都行），沒辦法增加 instance variable（其實也不是完全不行，只是可能要用一些怪招，不過如果要做到這種程度，是不是該考慮直接用一般的繼承就好？）。使用起來的樣子：

```objc
// 建立 Book 物件
Book *b = [[Book alloc] init];

// instance method from category
[b printRetainCount];

// 用完放掉
[b release];

// class method from category
[Book sayHello];
```

上面這段程式碼如果一般的情況下，在編譯階段就會跳出警告（認不得 printRetainCount 跟 sayHello 這兩個方法），硬是執行的話就會直接錯誤。但因為我們已經有了 category 的加持，所以執行結果會是：

    The retain count is 1
    Hello everybody!

因為 category 是在原來的類別裡加功能，所以你可能會想萬一原來的類別裡有個跟你的 category 同名的方法怎麼辦? 答案是，**會以 category 的定義為主**，也就是原來類別裡的那個方法就被你蓋掉了，當然這不見得是你想要的結果。所以通常這種方法擴充的，會建議可以在前面加個 prefix，避免跟原來的方法有重複而造成不幸的結果。

另外，前面有篇提到 [protocol](/2010/12/11/protocol-in-objective-c/) 的文章，也提到了 `informal protocol`，其實它就是一種依附在 NSObject 上，但是沒有把功能實作出來的 category。一般的 protocol 必需乖乖實作所有 `@required` 的方法（在 Objective-C 2.0 以前全部預設都是 `@required`），但在 informal protocol 並沒有強制規定全部都要實作出來。事實上，在 Objective-C 2.0 之後才在 protocol 裡加進來的@optional 語法，就是打算用來取代 informal protocol 的，在比較新版本的 SDK 大多都是用標準的 protocol 在寫了。

