讀《招聘一個靠譜的 iOS》-- 代碼風格

題目:
嘗試修改以下代碼中的風格錯誤:
這裏寫圖片描述

———————————————————我是分割線——————————————————–

博主在平時code時一直很注重自己的代碼風格,固在修改此題可以說是信心十足,但修改下來也就發現9處錯誤,還只是一些淺顯的“硬傷”,沒有什麼技術含量,而結合網上的一些錯誤修改卻發現十多條的“可優化部分”!
真是平時沒有嚴格要求住自己,纔會讓自己把一些錯誤的事情當做正確的事情來執行!

———————————————————我是分割線——————————————————–
先附上網上的一種優秀的修改方法 (稍加修改版):

typedef NS_ENUM(NSInteger, DBSex) {
    DBSexMan,
    DBSexWoman
};

@interface DBUser : NSObject<NSCopying>

@property (nonatomic, copy  , readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) DBSex sex;

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(DBSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;

@end

下面對具體修改的地方,分兩部分做下介紹:硬傷部分和優化部分
。因爲硬傷部分沒什麼技術含量,爲了節省大家時間,放在後面講,大神請直接看優化部分。

可優化部分(代碼由上往下看):
1、enum
enum官方給出的推薦方法中已經摒棄簡單的typedef enum這種寫法了,而是用NS_ENUMNS_OPTIONS 宏來定義枚舉類型,參見官方的 Adopting Modern Objective-C 一文:
舉例:

//定義一個枚舉
typedef NS_ENUM(NSInteger, DBSex) {
    DBSexMan,
    DBSexWoman
};

2、一般類名需要在加上前綴,用以分割項目中的所屬模塊或者與第三方類區分

3、熟悉定義的時候優於原子屬性(nonatomic)在絕大多數情況下是需要的,所以統一寫在第一位,觀察下面有該類的初始化方法,固猜測此此類中屬性是不允許隨意被外界修改的(需要修改需要有特定的方法),所以在屬性中需要加上readonly屬性。

得出修改結果:

@property (nonatomic, copy  , readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) DBSex sex;

注:後面的文章會說到爲什麼要把strong改成copy,大概可以理解爲指針本身的地址指針指向的地址的區別,說白了就是淺複製和深複製的區別。

4、NSIntegerNSUIntegerNSTimeIntervalCGFloatintfloatunsigned

int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
動畫時間 -> NSTimeInterval

從命名所表達的意思來看,屬性age應該代表的是歲數,而歲數爲非負整數,所以使用NSUInteger優於int。

這樣做的是基於64-bit 適配考慮,詳情可參考出題者的博文《64-bit Tips》

5、方法命名不規範

-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;

觀察方法名,不難理解到此方法適用於用參數來初始化一個實力DBUser方法,所以:
1) 返回類型id改爲instancetype會更加明瞭;
2) 名稱用init足以表達意思initUserModel會略顯臃腫;
3) 參數需要遵循原則:**with連接第一個獨立個參數,相對獨立參數用and連接,並列參數不要任何任何動詞連接。**(此處僅代表博主個人意見,如有不同,請指出!)

//錯誤,不要使用"and"來連接參數
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
//錯誤,不要使用"and"來闡明有多個參數
- (instancetype)initWithName:(CGFloat)width andAge:(CGFloat)height;
//正確,使用"and"來表示兩個相對獨立的操作
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;

4) 初始化時漏掉參數sex,在不太改動原來方法的情況下,應該拆分爲兩個獨立的方法。

最後得出結果:

- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(DBSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;

doLogIn方法命名不規範:添加了多餘的動詞前綴。 請牢記:

如果方法表示讓對象執行一個動作,使用動詞打頭來命名,注意不要使用do,does這種多餘的關鍵字,動詞本身的暗示就足夠了。

6、MVC而非MVVM
由於博主代碼風格一值都是使用MVVM,所以對此不做過多評價,所以貼出大神原話:

doLogIn方法不應寫在該類中:雖然LogIn的命名不太清晰,但筆者猜測是login的意思,而登錄操作屬於業務邏輯,觀察類名UserModel,以及屬性的命名方式,應該使用的是MVC模式,並非MVVM,在MVC中業務邏輯不應當寫在Model中。(如果是MVVM,拋開命名規範,UserModel這個類可能對應的是用戶註冊頁面,如果有特殊的業務需求,比如:login對應的應當是註冊並登錄的一個Button,出現login方法也可能是合理的。)

注:即使是在MVVM中,login這種方法也不應該出現再次出,此類偏向於數據模型,並不屬於業務邏輯範疇。

硬傷部分

1.在-和(void)之間應該有一個空格
2.enum中駝峯命名法和下劃線命名法混用錯誤:枚舉類型的命名規則和函數的命名規則相同:命名時使用駝峯命名法,勿使用下劃線命名法。
3.enum左括號前加一個空格,或者將左括號換到下一行
4.enum右括號後加一個空格
5.UserModel :NSObject 應爲UserModel : NSObject,也就是:右側少了一個空格。
6.@interface與@property屬性聲明中間應當間隔一行。
7.兩個方法定義之間不需要換行,有時爲了區分方法的功能也可間隔一行,但示例代碼中間隔了兩行。
8.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名與參數之間多了空格。而且- 與(id)之間少了空格。
9.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名與參數之間多了空格:(NSString*)name前多了空格。
10.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中(NSString*)name,應爲(NSString *)name,少了空格。
11.doLogIn方法命名不清晰:筆者猜測是login的意思,應該是粗心手誤造成的。
12.第二個@property中assign和nonatomic調換位置。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章