99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

iOS學(xué)習(xí)之創(chuàng)建對(duì)象方法詳解

發(fā)布時(shí)間:2017-02-13 12:54  回復(fù):0  查看:2014   最后回復(fù):2017-02-13 12:54  
在寫 ios開(kāi)發(fā)中,怎么樣去 new 一個(gè)新對(duì)象出來(lái),都有一些講究在里面。使用不同的姿勢(shì)去創(chuàng)建對(duì)象,對(duì)后期維護(hù)所造成的影響會(huì)存在細(xì)微的差別。
  init 創(chuàng)建
  在之前一篇分析 iOS 代碼耦合的文章中,提到過(guò)當(dāng)我們給一個(gè)對(duì)象的 property 賦值的時(shí)候,通過(guò) init 方法傳入?yún)?shù)來(lái)初始化 property 會(huì)讓我們的代碼更可靠。
  有些人在定義帶 property 的 class 的時(shí)候,會(huì)這樣定義:
  @interface User : NSObject@property (nonatomic, strong) NSNumber* userID;@end
  使用的時(shí)候如下:
  User* user = [[User alloc] init];user.userID = @1000;
  尤其是在定義 model 的時(shí)候,很容易寫出這種,先 init,而后挨個(gè)給 property 賦值的代碼。這種代碼的問(wèn)題在于 property 對(duì)于外部是可寫的,property 處于隨時(shí)可能變化的狀態(tài)。之前不少篇文章中都強(qiáng)調(diào)過(guò) immutable 的重要性,同樣對(duì)于一個(gè) class,我們也應(yīng)該優(yōu)先考慮設(shè)計(jì)成 immutable 的。
  initWith 創(chuàng)建
  如果將 property 都設(shè)置成 readonly 的,或者不暴露 property,property 的賦值都通過(guò) initWith 的方式來(lái)初始化,就可以得到一個(gè)具備 immutable 的 class 定義了,具體到上面的例子代碼如下:
  //User.h@interface User : NSObject@property (nonatomic, strong, readonly) NSNumber* userID;
  - (instancetype)initWithUserID:(NSNumber*)uid;@end
  //User.m@implementation User
  - (instancetype)initWithUserID:(NSNumber*)uid {
  self = [super init];
  if (!self) {
  return nil;
  }
  _userID = uid;
  return self;
  }@end
  userID 在 .h 文件當(dāng)中是 readonly 的,userID 只有一次被賦值的機(jī)會(huì),即在 User 的 initWith 方法中。這種方式的好處是一旦 User 對(duì)象創(chuàng)建完畢之后,就處于 immutable 的狀態(tài),property 都是不可修改的,安全可靠。
  Designated initializer
  Apple 為了方便開(kāi)發(fā)者使用 init 方法,引入了一種名為 designated initializer 的 pattern。主要用來(lái)管理當(dāng)一個(gè) class 擁有多個(gè) property 需要賦值的場(chǎng)景。比如上面我們的 User 類:
  @interface User : NSObject@property (nonatomic, strong, readonly) NSNumber* userID;@property (nonatomic, strong, readonly) NSString* userName;@property (nonatomic, strong, readonly) NSString* signature;@end
  有些場(chǎng)景需要初始化 userID 和 userName,而有些場(chǎng)景只需要初始化 userID 和 signature,所以我們需要提供多個(gè) initWith 方法給不同的場(chǎng)景使用。為了管理 initWith 方法,Apple 將 init 方法分為兩種類型:designated initializer 和 convenience initializer (又叫 secondary initializer) 。
  designated initializer 只有一個(gè),它會(huì)為 class 當(dāng)中每個(gè) property 都提供一個(gè)初始值,是最完整的 initWith 方法。convenience initializer 則可以有很多個(gè),它可以選擇只初始化部分的 property。convenience initializer 最后到會(huì)調(diào)用到 designated initializer,所以 designated initializer 也可以叫做 final initializer。
  無(wú)論我們定義何種類型的 class,給 class 中的每個(gè) property 都賦予一個(gè)初始值是個(gè)很好的習(xí)慣,可以避免掉一些意外的 bug 產(chǎn)生,這也是 designated initializer 的重要職責(zé)。
  在實(shí)際的項(xiàng)目當(dāng)中,一個(gè) class 的 property 數(shù)目可能會(huì)隨著業(yè)務(wù)的增長(zhǎng)而增加,最后的結(jié)果就是會(huì)生成越來(lái)越多的 convenience initializer。上述的 User 類,如果是 3 個(gè) property,極端的情況下最多可以有 7 個(gè) init 方法。Peak君在閱讀代碼的時(shí)候,也確實(shí)看到過(guò)有些 class 定義了一連串整整齊齊擺放的 init 方法,代碼雖然看著規(guī)范,但顯得啰嗦,而且每次需要肉眼搜索適合的 init 方法。
  其實(shí)我們還可以用另一種姿勢(shì)來(lái) init 我們的對(duì)象。
  Builder pattern
  最初是在學(xué)習(xí) Android 的時(shí)候,發(fā)現(xiàn)這個(gè) builder pattern 也可以用來(lái)構(gòu)建對(duì)象,而且可以很好的解決 init 方法過(guò)多難以管理的問(wèn)題。先來(lái)看下如何實(shí)現(xiàn),顧名思義,builder pattern 使用另一個(gè)名為 builder 的類來(lái)創(chuàng)建我們的目標(biāo)對(duì)象,還是上面的例子,代碼如下:
  //UserBuilder.h@interface UserBuilder : NSObject@property (nonatomic, strong, readonly) NSNumber* userID;@property (nonatomic, strong, readonly) NSString* userName;@property (nonatomic, strong, readonly) NSString* signature;
  - (UserBuilder*)userID:(NSNumber*)userID;
  - (UserBuilder*)userName:(NSString*)userName;
  - (UserBuilder*)signature:(NSString*)signature;@end
  //UserBuilder.m@implementation UserBuilder
  - (UserBuilder*)userID:(NSNumber*)userID {
  _userID = userID;
  return self;
  }
  - (UserBuilder*)userName:(NSString*)userName {
  _userName = userName;
  return self;
  }
  - (UserBuilder*)signature:(NSString*)signature {
  _signature = signature;
  return self;
  }@end
  接下來(lái) User 的 init 方法從 Builder 中獲取 property 的初始值:
  //User.h@interface User : NSObject@property (nonatomic, strong, readonly) NSNumber* userID;@property (nonatomic, strong, readonly) NSString* userName;@property (nonatomic, strong, readonly) NSString* signature;
  - (instancetype)initWithUserBuilder:(UserBuilder*)builder;@end
  //User.m@implementation User
  - (instancetype)initWithUserBuilder:(UserBuilder*)builder {
  self = [super init];
  if (!self) {
  return nil;
  }
  _userID = builder.userID;
  _userName = builder.userName;
  _signature = builder.signature;
  return self;
  }@end
  如果要?jiǎng)?chuàng)建 User 對(duì)象,則按照這種方式:
  UserBuilder* builder = [[[[UserBuilder new] userName:@"peak"] userID:@1000] signature:@"roll"];
  User* user = [[User alloc] initWithUserBuilder:builder];
  這樣我們避免了書(shū)寫多個(gè) init 方法,同樣 User 對(duì)象也是 immutable 的,也做到了只在 init 方法中做一次賦值操作,每個(gè)場(chǎng)景都可以按照自己的需求初始化部分 property,當(dāng)然最后我們需要在initWithUserBuilder 中為每一個(gè) property 賦值, initWithUserBuilder 扮演的角色類似于 designated initializer。
  追求代碼美感的同學(xué)可能發(fā)現(xiàn)了, UserBuilder 的創(chuàng)建語(yǔ)法很丑陋,多個(gè) [ ] 套嵌使用。為了讓代碼更好看一些,我們也可以使用 block 來(lái)創(chuàng)建:
  User* user = [User userWithBlock:^(UserBuilder* builder) {
  builder.userName = @"peak";
  builder.userID = @1000;
  builder.signature = YES;
  }];
  builder pattern 在 Android 平臺(tái)使用的比較多,我在 iOS 平臺(tái)上鮮少有看到使用的場(chǎng)景。builder pattern 的不足之處也比較明顯,需要另外定義一個(gè) builder 類,多寫一些代碼(property 基本都重復(fù)寫了一遍)。個(gè)人覺(jué)得,在 property 數(shù)量較多,初始化的場(chǎng)景也比較多的時(shí)候,在 iOS 上使用 builder pattern 也會(huì)是個(gè)不錯(cuò)的方案。
  designated initializer vs builder pattern,這二者之間的不同其實(shí)很好的體現(xiàn)了語(yǔ)言本身的差異性。學(xué)習(xí)過(guò) java 的同學(xué)就能明白,在 java 的世界中,一切都是可以被封裝成對(duì)象的,使用 java 的時(shí)候,經(jīng)常要定義各式各樣的輔助類來(lái)完成某個(gè)任務(wù),好處是封裝度高,類職責(zé)劃分粒度小,缺點(diǎn)是類太多,有時(shí)候會(huì)為了封裝而封裝,某些場(chǎng)景代碼反而不夠直觀。
  總結(jié)
  簡(jiǎn)單梳理了下創(chuàng)建對(duì)象的不同姿勢(shì),希望對(duì)大家有些幫助。


來(lái)源:MrPeak技術(shù)分享
您還未登錄,請(qǐng)先登錄

熱門帖子

最新帖子

?