在平常的開發中,可能會碰到這樣的需求:給某個類增長方法。好比說,須要給NSString類增長一個打印的方法。固然,咱們能夠新建一個類好比TestString,並繼承NSString類,在新的類TestString中實現 displayString方法。可是,這種方法有一個明顯的缺陷是:只有 TestString類有該方法,NSString類的其餘子類,好比 NSMutableString 不能使用該方法。可否給 NSString 類增長一個方法,讓NSString以及NSString的全部子類均可以使用呢?答案能夠的,Category能夠完美的解決這個問題。編程
Objective-C 中使用Category的語法是使用 @interface關鍵字,和定義一個標準的類很是相似,不過不是使用冒號(:,:是繼承一個類時使用),而是使用 (),以下:app
@interface NSString (PlayString) - (void)playString:(NSString *)content; @end
其中:括號內的 PlayStirng 是Category的名稱。函數
能夠爲任何一個類增長 Category,即便看不到這個類的源代碼。當爲一個類增長Cateogry後,這個類以及這個類的全部子類均可以使用Category中的方法。在運行時,Category 中的方法和類中原來的代碼是沒有區別的。好比說,上例中,NSString 類增長Cateogry,Category中定義了 playString方法,該方法的實現以下:this
- (void)playString:(NSString *)content { NSLog(@"the content is %@",content); }
這樣,NSString類的實例對象,以及NSString類的全部子類的實例對象,均可以使用 playString方法。以下:spa
NSString *myString = @"this is original NSString"; [myString playString:myString]; NSMutableString *mutString = [[NSMutableString alloc] init]; [mutString appendString:@"this is a subclass of NSString"]; [mutString playString:mutString];
除了給一個類增長方法外,Category 還有如下兩種使用場景:code
1:將一個大的、複雜的類文件拆分紅幾個小的類文件。對象
2:多我的開發同一個類文件時,可使用Category,分別開發本身的功能。blog
1:Category中方法的命名。繼承
(1):儘可能不要和原始類中的方法重名,儘管這樣是合法的,可是和原始類中的方法重名絕對不是一個好的編程習慣。由於這樣形成的後果是,不管是原始類,仍是原始類的子類,都沒法使用原始類中的那個方法。一般來講,想要覆蓋父類中某個方法的狀況,更適合用繼承來實現,而不是Category。內存
(2):當一個原始類有多個 Category 時,各個Category 中的方法名要保持相異。儘管多個Category中方法名重複不會提醒錯誤,可是會發生一些莫名其妙的錯誤。多個Category中的方法名重複時,每一個Category都會向原始類中增長一個函數,這樣在運行時,所調用的方法和咱們所指望的可能會不一致。這種狀況下,具體調用哪一個Category中的方法和編譯器是相關的。
2:Category中不能增長實例變量。雖然在Category中能夠增長屬性,可是在 .m文件中,編譯器不會自動合成實例變量,以及訪問實例變量的 getter/setter 方法。想要爲某個原始類增長實例變量,這種狀況能夠用繼承來實現。
實際上,Objective-C 中的類通過編譯後,在內存中都有一個方法列表,方法列表指向的是該方法的代碼塊地址。當向某個方法發送消息時,就從方法列表中尋找方法。舉例來講有一個類 Person,該類通過編譯後生成的方法列表是: setName、getName、getSex ……。如今該類增長一個Category,Category中也實現了方法getName,則再次通過編譯後,生成的方法列表是: setName、getName(Category)、getName(原始類)、getSex ……,當給getName方法發送消息時,從類的方法列表中尋找,找到第一個getName方法時,就不在繼續往下尋找,這樣使用的永遠是 Category中實現的 getName 方法。這也是爲什麼要注意Category中方法命名的緣由。