一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - IOS - iOS創建對象的不同姿勢詳解

iOS創建對象的不同姿勢詳解

2021-03-05 17:06mrpeak IOS

這篇文章主要介紹了iOS創建對象的不同姿勢,文中介紹的很詳細,對大家具有一定的參考價值,有需要的朋友們下面來一起學習學習吧。

前言

在寫 iOS 代碼的時候,怎么樣去 new 一個新對象出來,都有一些講究在里面。使用不同的姿勢去創建對象,對后期維護所造成的影響會存在細微的差別。

init 創建

在之前一篇分析 iOS 代碼耦合的文章中,提到過當我們給一個對象的 property 賦值的時候,通過 init 方法傳入參數來初始化 property 會讓我們的代碼更可靠。

有些人在定義帶 property 的 class 的時候,會這樣定義:

?
1
2
3
@interface User : NSObject
@property (nonatomic, strong) NSNumber*     userID;
@end

使用的時候如下:

?
1
2
User* user = [[User alloc] init];
user.userID = @1000;

尤其是在定義 model 的時候,很容易寫出這種,先 init,而后挨個給 property 賦值的代碼。這種代碼的問題在于 property 對于外部是可寫的,property 處于隨時可能變化的狀態。之前不少篇文章中都強調過 immutable 的重要性,同樣對于一個 class,我們也應該優先考慮設計成 immutable 的。

initWith 創建

如果將 property 都設置成 readonly 的,或者不暴露 property,property 的賦值都通過 initWith 的方式來初始化,就可以得到一個具備 immutable 的 class 定義了,具體到上面的例子代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//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 文件當中是 readonly 的,userID 只有一次被賦值的機會,即在 User 的 initWith 方法中。這種方式的好處是一旦 User 對象創建完畢之后,就處于 immutable 的狀態,property 都是不可修改的,安全可靠。

Designated initializer

Apple 為了方便開發者使用 init 方法,引入了一種名為 designated initializer 的 pattern。主要用來管理當一個 class 擁有多個 property 需要賦值的場景。比如上面我們的 User 類:

?
1
2
3
4
5
@interface User : NSObject
@property (nonatomic, strong, readonly) NSNumber*     userID;
@property (nonatomic, strong, readonly) NSString*     userName;
@property (nonatomic, strong, readonly) NSString*     signature;
@end

有些場景需要初始化 userID 和 userName,而有些場景只需要初始化 userID 和 signature,所以我們需要提供多個 initWith 方法給不同的場景使用。為了管理 initWith 方法,Apple 將 init 方法分為兩種類型:designated initializer 和 convenience initializer (又叫 secondary initializer) 。

designated initializer 只有一個,它會為 class 當中每個 property 都提供一個初始值,是最完整的 initWith 方法。convenience initializer 則可以有很多個,它可以選擇只初始化部分的 property。convenience initializer 最后到會調用到 designated initializer,所以 designated initializer 也可以叫做 final initializer。

無論我們定義何種類型的 class,給 class 中的每個 property 都賦予一個初始值是個很好的習慣,可以避免掉一些意外的 bug 產生,這也是 designated initializer 的重要職責。

在實際的項目當中,一個 class 的 property 數目可能會隨著業務的增長而增加,最后的結果就是會生成越來越多的 convenience initializer。上述的 User 類,如果是 3 個 property,極端的情況下最多可以有 7 個 init 方法。Peak君在閱讀代碼的時候,也確實看到過有些 class 定義了一連串整整齊齊擺放的 init 方法,代碼雖然看著規范,但顯得啰嗦,而且每次需要肉眼搜索適合的 init 方法。

其實我們還可以用另一種姿勢來 init 我們的對象。

Builder pattern

最初是在學習 Android 的時候,發現這個 builder pattern 也可以用來構建對象,而且可以很好的解決 init 方法過多難以管理的問題。先來看下如何實現,顧名思義,builder pattern 使用另一個名為 builder 的類來創建我們的目標對象,還是上面的例子,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//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

接下來 User 的 init 方法從 Builder 中獲取 property 的初始值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//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

如果要創建 User 對象,則按照這種方式:

?
1
2
UserBuilder* builder = [[[[UserBuilder new] userName:@"peak"] userID:@1000] signature:@"roll"];
User* user = [[User alloc] initWithUserBuilder:builder];

這樣我們避免了書寫多個 init 方法,同樣 User 對象也是 immutable 的,也做到了只在 init 方法中做一次賦值操作,每個場景都可以按照自己的需求初始化部分 property,當然最后我們需要在 initWithUserBuilder 中為每一個 property 賦值, initWithUserBuilder 扮演的角色類似于 designated initializer。

追求代碼美感的同學可能發現了, UserBuilder 的創建語法很丑陋,多個 [ ] 套嵌使用。為了讓代碼更好看一些,我們也可以使用 block 來創建:

?
1
2
3
4
5
User* user = [User userWithBlock:^(UserBuilder* builder) {
 builder.userName = @"peak";
 builder.userID = @1000;
 builder.signature = YES;
}];

builder pattern 在 Android 平臺使用的比較多,我在 iOS 平臺上鮮少有看到使用的場景。builder pattern 的不足之處也比較明顯,需要另外定義一個 builder 類,多寫一些代碼(property 基本都重復寫了一遍)。個人覺得,在 property 數量較多,初始化的場景也比較多的時候,在 iOS 上使用 builder pattern 也會是個不錯的方案。

designated initializer vs builder pattern,這二者之間的不同其實很好的體現了語言本身的差異性。學習過 java 的同學就能明白,在 java 的世界中,一切都是可以被封裝成對象的,使用 java 的時候,經常要定義各式各樣的輔助類來完成某個任務,好處是封裝度高,類職責劃分粒度小,缺點是類太多,有時候會為了封裝而封裝,某些場景代碼反而不夠直觀。

經讀者反饋,原來這篇文章的主題已經被寫過了。看過之后發現比我寫的更全面,推薦大家閱讀,傳送門

總結

以上就是這篇文章的全部內容了,本文簡單梳理了下創建對象的不同姿勢,希望對大家有些幫助。如果有疑問大家可以留言交流。

原文鏈接:http://mrpeak.cn/blog/ios-init/

延伸 · 閱讀

精彩推薦
  • IOSiOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果

    iOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果

    這篇文章主要介紹了iOS自定義UICollectionViewFlowLayout實現圖片瀏覽效果的相關資料,需要的朋友可以參考下...

    jiangamh8882021-01-11
  • IOSiOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和Masonry簡單使用)

    iOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和

    這篇文章主要介紹了iOS中滑動控制屏幕亮度和系統音量(附加AVAudioPlayer基本用法和Masonry簡單使用)的相關資料,需要的朋友可以參考下...

    CodingFire13652021-02-26
  • IOS詳解iOS中多個網絡請求的同步問題總結

    詳解iOS中多個網絡請求的同步問題總結

    這篇文章主要介紹了詳解iOS中多個網絡請求的同步問題總結,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧...

    liang199111312021-03-15
  • IOSiOS實現控制屏幕常亮不變暗的方法示例

    iOS實現控制屏幕常亮不變暗的方法示例

    最近在工作中遇到了要將iOS屏幕保持常亮的需求,所以下面這篇文章主要給大家介紹了關于利用iOS如何實現控制屏幕常亮不變暗的方法,文中給出了詳細的...

    隨風13332021-04-02
  • IOSiOS中MD5加密算法的介紹和使用

    iOS中MD5加密算法的介紹和使用

    MD5加密是最常用的加密方法之一,是從一段字符串中通過相應特征生成一段32位的數字字母混合碼。對輸入信息生成唯一的128位散列值(32個字符)。這篇文...

    LYSNote5432021-02-04
  • IOSiOS中UILabel實現長按復制功能實例代碼

    iOS中UILabel實現長按復制功能實例代碼

    在iOS開發過程中,有時候會用到UILabel展示的內容,那么就設計到點擊UILabel復制它上面展示的內容的功能,也就是Label長按復制功能,下面這篇文章主要給大...

    devilx12792021-04-02
  • IOSiOS開發技巧之狀態欄字體顏色的設置方法

    iOS開發技巧之狀態欄字體顏色的設置方法

    有時候我們需要根據不同的背景修改狀態欄字體的顏色,下面這篇文章主要給大家介紹了關于iOS開發技巧之狀態欄字體顏色的設置方法,文中通過示例代碼...

    夢想家-mxj8922021-05-10
  • IOSiOS開發之視圖切換

    iOS開發之視圖切換

    在iOS開發中視圖的切換是很頻繁的,獨立的視圖應用在實際開發過程中并不常見,除非你的應用足夠簡單。在iOS開發中常用的視圖切換有三種,今天我們將...

    執著丶執念5282021-01-16
主站蜘蛛池模板: 欧美日韩国产亚洲人成 | 亚洲天堂伦理 | 6个老师的尿奴 | 五月色婷婷网在线观看 | 欧美日韩精品亚洲精品v18 | 国产精品嫩草影院在线 | 国产精品久久久久久爽爽爽 | 乖女的嫩奶水h文孕妇 | 色猪视频| 久久国产乱子伦免费精品 | 国产麻豆麻豆 | 人与动人物性行为zozo共患病 | 色噜噜狠狠色综合 | 紧身裙女教师波多野结衣 | 狠狠撸在线播放 | 疯狂激吻添下边小说 | 青青国产精品 | 亚州日韩精品AV片无码中文 | 秀婷程仪公欲息肉婷在线观看 | 女同学高中你下面好紧 | 深夜在线影院 | 亚洲精品第一国产综合 | 国产一区二区在线观看视频 | 久久亚洲免费视频 | 久久精品国产免费播放 | 处女摘花视频 | 顶级尤物极品女神福利视频 | 欧美国产影院 | 日本一卡二卡3卡四卡网站精品 | 亚洲精品午夜久久aaa级久久久 | 国产乱妇无码大片在线观看 | caopo视频进入离开 | 国产欧美精品一区二区三区–老狼 | 国产成人久久精品推最新 | 40分钟在线观看免费 | 亚洲乱码尤物193yw在线播放 | tk白丝丨vk | 99午夜| 日产中文乱码卡一卡二 | 国产欧美一区二区精品久久久 | 亚洲不卡高清免v无码屋 |