分類(category)和類擴展(extension)

一、分類(類別):

背景:

在大型項目,企業(yè)級開發(fā)中多人同時維護同一個類,此時程序員A因為某項需求只想給當前類currentClass添加一個方法newMethod,那該怎么辦呢?

最簡單粗暴的方式是把newMethod添加到currentClass中,然后直接實現(xiàn)該方法就OK了。

但考慮到OC是單繼承的,子類可以擁有父類的方法和屬性。

如果把newMethod寫到currentClass中,那么currentClass的子類也會擁有newMethod。但真正的需求是只需要currentClass擁有newMethod,而currentClass的子類不會擁有。

蘋果為了解決這個問題,就引入了分類(Category)的概念。

分類運用場景舉例:

舉例:想要收集每個頁面的啟動時間。

問題1:

項目中已經(jīng)有上百個頁面了,如果一個一個的加,浪費時間不說,以后增加了新頁面,還需要添加方法。

解決方法:

我們可以發(fā)現(xiàn)頁面都繼承了UIViewController,想要在每個頁面都執(zhí)行的代碼,可以寫在這些頁面的父類中。我們可以把代碼寫在UIViewController中;出現(xiàn)問題2。

問題2:

UIViewController是官方類,我們只能調(diào)用期接口,并不能修改他的實現(xiàn)。

解決方法:使用分類(category)。

問題3:

本類是系統(tǒng)的類,這里是UIViewController,我們可以使用分類擴展他的方法,也可以重寫他的方法,可是我需要在調(diào)用的地方加頭文件,所有子類都寫頭文件和直接在子類寫方法沒有什么區(qū)別,怎么樣可以使得不寫頭文件,子類就能調(diào)用我們寫的代碼呢?

回答:

我們可以進行方法交換(這樣可以不必在調(diào)用的地方增加頭文件),從而使得在實現(xiàn)的時候調(diào)用重寫的方法。

概念

分類(Category)是OC中的特有語法,它是表示一個指向分類的結構體的指針。原則上它只能增加方法,不能增加成員(實例)變量。

作用

1.可以在不修改原來類的基礎上,為一個類擴展方法。

2最主要的用法:給系統(tǒng)自帶的類擴展方法。

注意

1.分類中只能添加“方法”,不能增加成員變量,因為分類的結構體指針中,沒有屬性列表,只有方法列表;

如果分類中聲明了一個屬性,那么分類只會生成這個屬性的set、get方法聲明,也就是不會有實現(xiàn)(調(diào)用set方法給該屬性賦值會崩潰,調(diào)用get方法獲取屬性值也會崩潰);

原則上講它只能添加方法,不能添加屬性即成員變量,實際上可以通過其他方式(通過getter,setter手段即運行時進行添加)添加屬性;

2.分類中的可以寫@property, 但不會生成setter/getter方法, 也不會生成實現(xiàn)以及私有的成員變量(編譯時會報警告);

3.分類中可以訪問原來類中的成員變量,但是只能訪問@protect和@public形式的變量。如果想要訪問本類中的私有變量,分類和子類一樣,只能通過方法來訪問;

4.如果分類中有和原有類同名的方法, 會優(yōu)先調(diào)用分類中的方法, 就是說會忽略原有類的方法。所以同名方法調(diào)用的優(yōu)先級為分類 > 本類 > 父類。因此在開發(fā)中盡量不要覆蓋原有類;

5.如果多個分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時候執(zhí)行誰由編譯器決定;編譯器會執(zhí)行最后一個參與編譯的分類中的方法。

1.分類不能添加屬性的實質(zhì)原因:

我們知道在一個類中用@property聲明屬性,編譯器會自動幫我們生成_成員變量和setter/getter,但分類的指針結構體中,根本沒有屬性列表。所以在分類中用@property聲明屬性,既無法生成_成員變量也無法生成setter/getter。 因此結論是:我們可以用@property聲明屬性,編譯和運行都會通過,只要不使用程序也不會崩潰。但如果調(diào)用了_成員變量和setter/getter方法,報錯就在所難免了。

如果調(diào)用了,報錯如下:

//普通聲明,不會自動生成setter/getter,

// ?programmer.nameWithoutSetterGetter = @"無setter/getter"; //調(diào)用setter,編譯成功,運行報錯為:(-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7f9de358fd70')

// ?NSLog(@"%@",programmer.nameWithoutSetterGetter);? ? ? ? ? //調(diào)用getter,編譯成功,運行報錯為-[Programmer setNameWithSetterGetter:]: unrecognized selector sent to instance 0x7fe22be11ea0'

// ?NSLog(@"%@",_nameWithoutSetterGetter);? ? ? ? //這是調(diào)用_成員變量,錯誤提示為:(Use of undeclared identifier'_nameWithoutSetterGetter')

2.報錯的根本原因是使用了系統(tǒng)沒有生成的setter/getter方法,我們可以手動添加setter/getter來避免崩潰,完成調(diào)用;

由于OC是動態(tài)語言,方法真正的實現(xiàn)是通過runtime完成的,雖然系統(tǒng)不給我們生成setter/getter,但我們可以通過runtime手動添加setter/getter方法。

通過運行時手動添加:

#import

static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey";//定義一個key值

@implementation Programmer (Category)?

//運行時實現(xiàn)setter方法

- (void)setNameWithSetterGetter:(NSString*)nameWithSetterGetter

?{?

?objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);

}

//運行時實現(xiàn)getter方法

- (NSString*)nameWithSetterGetter

?{

returnobjc_getAssociatedObject(self, &nameWithSetterGetterKey);

}

@end

運行:

//通過runtime實現(xiàn)了setter/getter

programmer.nameWithSetterGetter =@"有setter/getter";//調(diào)用setter,成功NSLog(@"%@",programmer.nameWithSetterGetter);//調(diào)用getter,成功 NSLog(@"%@",_nameWithSetterGetter); //這是調(diào)用_成員變量,錯誤提示為:(Use of undeclared identifier '_nameWithSetterGetter')

但是注意,以上代碼僅僅是手動實現(xiàn)了setter/getter方法,但調(diào)用_成員變量依然報錯。

二、類擴展:

Extension是Category的一個特例。類擴展與分類相比只少了分類的名稱,所以稱之為“匿名分類”。

作用:

能為某個類附加額外的屬性,成員變量,方法聲明;

一般的類擴展寫到.m文件中;

一般的私有屬性寫到類擴展;

三、分類和類擴展的區(qū)別:

1.分類格式:

@interface待擴展的類(分類的名稱)

@end

@implementation待擴展的名稱(分類的名稱)

@end

類擴展格式:

@interfaceXXX()

//私有屬性

//私有方法(如果不實現(xiàn),編譯時會報警,Method definition for 'XXX' not found)

@end

2.分類原則上只能添加“方法”,不能增加成員變量,但是可以通過runtime解決無setter/getter的問題,進而添加屬性;類擴展不僅可以增加方法,還可以增加實例變量(或者屬性),只是該實例變量默認是@private類型的;

3.類擴展中添加的新方法,一定要實現(xiàn),沒被實現(xiàn),編譯器會報警;categorygory中沒有這種限制,類別中的方法沒被實現(xiàn)編譯器是不會有任何警告的。這是因為類擴展是在編譯階段被添加到類中,而類別是在運行時添加到類中。

4.通常來講,分類定義在.h文件中,但也可以定義.m文件中,此時分類的方法就變成私有方法;

類擴展可以定義在.m文件中,這種擴展方式中定義的變量都是私有的,也可以定義在.h文件中,這樣定義的代碼就是共有的,類擴展在.m文件中聲明私有方法是非常好的方式。

5.類擴展不能像類別那樣擁有獨立的實現(xiàn)部分(@implementation部分),也就是說,類擴展所聲明的方法必須依托對應類的實現(xiàn)部分來實現(xiàn)。

四、關于繼承和分類的使用:

以下情況,使用繼承:

1)新擴展的方法與原方法同名,但是還需要使用父類的實現(xiàn)。

2)擴展類的屬性

以下情況,使用類別:

1)針對系統(tǒng)特定類,例如:NSString,NSArray,NSNumber等。

2)針對自定義類,對于大型而復雜的類,為提高可維護性,把相關的方法分組到多個單獨的文件中。


?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容