類簇在iOS開(kāi)發(fā)中的應(yīng)用
類簇(class cluster)是一種設(shè)計(jì)模式,在Foundation Framework中被廣泛使用,舉個(gè)簡(jiǎn)單的例子
- NSArray *arr = [NSArray arrayWithObjects:@"foo",@"bar", nil];
- NSLog(@"arr class:%@", [arr class]);
- // output: __NSArrayI
顯然__NSArrayI
是一個(gè)私有類,來(lái)看看這個(gè)類的頭文件
- @interface __NSArrayI : NSArray {
- unsigned int _used;
- }
- //...
可以看出__NSArrayI
繼承了NSArray
。為什么要這么設(shè)計(jì)呢?拿NSNumber來(lái)舉個(gè)例子,我們都知道NSNumber可以存儲(chǔ)多種類型的數(shù)字,如Int/Float/Double等等,一種方式是把NSNumber作為基類,然后分別去實(shí)現(xiàn)各自的子類,像這樣:
初看起來(lái)也沒(méi)什么問(wèn)題,但如果子類很多,像這樣:
這對(duì)使用者來(lái)說(shuō)顯然不夠方便,得記住這么多類。如果使用類簇,問(wèn)題就變得簡(jiǎn)單了,把Number作為抽象基類,子類各自實(shí)現(xiàn)存取方式,然后在基類中定義多個(gè)初始化方式,像這樣:
現(xiàn)在只需要記住一個(gè)類就可以了。NSNumber
的初始化偽代碼大概像這樣:
- - (id)initWithBool
- {
- return [[__NSCFBoolean alloc]init];
- }
- - (id)initWithLong
- {
- return [[__NSCFNumber alloc]init];
- }
- //...
在iOS項(xiàng)目中的應(yīng)用
在開(kāi)發(fā)app時(shí)經(jīng)常會(huì)遇到表現(xiàn)和行為完全一樣,但數(shù)據(jù)源不一樣的情況。以花瓣app為例,同樣是瀑布流,可能來(lái)自我喜歡的圖片、某個(gè)畫(huà)板下的圖片、 某個(gè)用戶的圖片等等。如果為每一種表現(xiàn)方式都新建一個(gè)Controller,并且使用這個(gè)Controller來(lái)初始化,那么就會(huì)遇到最開(kāi)始提到的問(wèn)題: 子類太多,使用不便。這正好可以通過(guò)類簇來(lái)很方便地搞定。比如這樣:
- @implementation HBWaterfallViewController
- - (id)initWithLiked
- {
- return [[HBLikedViewController alloc]init];
- }
- - (id)initWithBoardID:(NSInteger)boardID
- {
- return [[HBBoardViewController alloc]initWithBoardID:boardID];
- }
- #pragma mark - 通用的方法
- - (PSUICollectionViewCell *)collectionView:(PSUICollectionView *)collectionView
- cellForItemAtIndexPath:(NSIndexPath *)indexPath
- {
- // ...
- }
- // ...
- #pragma mark - 每個(gè)子類需要實(shí)現(xiàn)的方法
- - (void)fetchMoreData
- {
- NSAssert(NO, @"子類需要實(shí)現(xiàn)此方法");
- }
使用起來(lái)類似這樣[[HBWaterfallViewController alloc]initWithBoardID:9527]
或 [[HBWaterfallViewController alloc]initWithLiked]
。如果有新的DataSource,新加一個(gè)初始化方法即可,對(duì)于使用者來(lái)說(shuō),打開(kāi)頭文件,看下init開(kāi)頭的方法就行了。
再舉個(gè)例子,現(xiàn)在很多應(yīng)用需要同時(shí)兼顧iOS6和iOS7,在表現(xiàn)上需要為不同的系統(tǒng)加載不同的圖片資源,最簡(jiǎn)單粗暴的方法就是各種if/else判斷,像這樣:
- if ([[UIDevice currentDevice]systemMajorVersion] < 7)
- {
- /* iOS 6 and previous versions */
- }
- else
- {
- /* iOS 7 and above */
- }
不夠優(yōu)雅,可以使用類簇的思想來(lái)去掉if/else判斷,把跟視圖具體元素?zé)o關(guān)的代碼放在基類,跟系統(tǒng)版本相關(guān)的代碼拆成兩個(gè)子類,然后在各自的類中加載相應(yīng)的資源。
- /* TestView.h */
- @interface TestView: UIView
- /* Common method */
- - ( void )test;
- @end
- /* TestView.m */
- @implementation TestView
- + (id)alloc
- {
- if ([self class]== [TestView class])
- {
- if ([[UIDevice currentDevice] systemMajorVersion] < 7)
- {
- return [TestViewIOS6 alloc];
- }
- else
- {
- return [TestViewIOS7 alloc];
- }
- }
- else
- {
- return [super alloc];
- }
- }
- - ( void )test
- {}
- @end
這里alloc
時(shí)并沒(méi)有返回TestView
類,而是根據(jù)系統(tǒng)版本返回TestViewIOS6
或 TestViewIOS7
。
- /* TestViewIOS6.m */
- @implementation TestViewIOS6: TestView
- - (void)drawRect: (CGRect)rect
- {
- /* Custom iOS6 drawing code */
- }
- @end
- /* TestViewIOS7.m */
- @implementation TestViewIOS7
- - (void)drawRect: (CGRect)rect
- {
- /* Custom iOS7 drawing code */
- }
- @end
小結(jié)
類簇的本質(zhì)其實(shí)是抽象工廠,類簇也可以有多個(gè)基類,如NSArray
, NSMutableArray
, 后者就是繼承的前者。它對(duì)一些「大同小異」的問(wèn)題,往往會(huì)有不錯(cuò)的效果。
參考
【移動(dòng)開(kāi)發(fā)視頻課程推薦】
- iOS培訓(xùn)之Objective-C基礎(chǔ)視頻教程(40集)
- Cocos2d-x從零開(kāi)始【5天掌握跨平臺(tái)游戲開(kāi)發(fā)利器】(12集)
- Objective C編程基礎(chǔ)(24集)
- Android技術(shù)輕松入門(mén)課程(12集)
- 微信開(kāi)放平臺(tái)-Android應(yīng)用接入(4集)
- Cocos2d-x跨平臺(tái)游戲開(kāi)發(fā)入門(mén)基礎(chǔ)(29集)
- iOS開(kāi)發(fā)視頻教程-iOS網(wǎng)絡(luò)編程【高級(jí)篇】(39集)
- 移動(dòng)應(yīng)用用戶體驗(yàn)設(shè)計(jì)高級(jí)課程(60集)
- 從零學(xué)習(xí)iOS開(kāi)發(fā)–UI多視圖(30集)
- iOS開(kāi)發(fā)視頻教程【基礎(chǔ)入門(mén)篇】