iOS開發(fā)中的代碼規(guī)范

Objective-C代碼編寫規(guī)范

1. 命名規(guī)范

我們盡可能遵守Apple的命名約定,其推薦使用長的、描述性強(qiáng)的方法名和變量名,使其閱讀起來更加清晰易懂。不能隨意使用縮寫,導(dǎo)致其他人員閱讀代碼困難。 附:The Coding Guide for Cocoa

1.1 前綴
項(xiàng)目名稱、類名、文件名都應(yīng)該保持一致的前綴名。根據(jù)Apple Guide的建議,類名前綴應(yīng)該使用2個(gè)英文字母以上最好,因?yàn)锳pple寫的框架都是使用2個(gè)英文字母開頭,使用3個(gè)字母能有效防止類名重復(fù),避免影響工程。

1.2 方法命名規(guī)范
方法一般不能用init、set開頭進(jìn)行命名,如果不是寫初始化方法不要用init進(jìn)行開頭;如果不是屬性的set方法,不要用set作為方法的前綴。

根據(jù)Cocoa命名方法規(guī)則,我們應(yīng)該遵守這幾個(gè)點(diǎn):
a. 使用小寫字母開頭,后面嵌套連接的字母使用大寫開頭。不過在寫 Category Method 的時(shí)候,我們比較習(xí)慣使用 JSD_method 的方式,而非遵守所有的方法命名規(guī)則 jsd_method,這個(gè)我覺得只要統(tǒng)一起來就好了。
b. 對于采取動作行為的方法,使用動詞開頭,但是不要直接使用do或者does;
c. 每個(gè)方法參數(shù)前必須帶有相同或者能清晰表達(dá)其原意的關(guān)鍵字;
d. 子類創(chuàng)建相對父類更加詳細(xì)功能的方法時(shí)應(yīng)該把新增參數(shù)添加在原有方法的后面;
e. 假如方法名過長的時(shí)候可以采用每個(gè)參數(shù)單獨(dú)占一行的規(guī)則,并保持每個(gè)參數(shù)分號:對齊的方式排列;
f. 實(shí)例方法和類方法 (-/+) 符號后面應(yīng)該保持一個(gè)空格,如:- (void)

1.3 控件命名規(guī)范
對于命名一定不要簡寫,UILabel結(jié)尾加上LabelUIImageView結(jié)尾加上ImageView等等讓其他人看名字就知道變量的用法和屬于什么控件。

1.4 Block 的命名規(guī)范
之前研究過很多的第三方命名,對于Apple官方的沒找到,有CallBack、CompleteBlock結(jié)尾的,還有CompletionHandler結(jié)尾的。我看到很多的結(jié)尾都是用CompletionHandler,大部分命名是Block,我們按照Block來命名。

例子:

eg 1. 對于#define宏命名,單詞應(yīng)全部大寫,單詞之間用_分割。
建議的寫法:
#define NS_AVAILABLE_IOS 3.0
不建議的寫法:
#define NSAvailableIos 3.0

eg 2. 類型常量
多用類型常量,少用#define。
建議的寫法:
const NSTimeInterval kAnimationDuration = 0.3;
不建議的寫法:
#define ANIMATION_DURATION 0.3

當(dāng)使用類型常量定義時(shí),若常量局限于某編譯單元,也就是實(shí)現(xiàn)文件里面,則使用k作為前綴,如:kCountdownTime;

若常量在類之外公開出來,則需要使用規(guī)定的類名作為前綴。如:SettingCountdownTime。

約定:在我們自己定義NSNotification的時(shí)候應(yīng)該把通知的名字定義為一個(gè)字符串常量,就像把我們暴露給其他類的字符串常量一樣。使用extern關(guān)鍵字將其在.h文件聲明,并且在.m文件對其定義。

.h聲明:

UIKIT_EXTERN NSString *const BWWillUpdateListNotification;
UIKIT_EXTERN const NSInteger MaxLeadCharCount;

.m實(shí)現(xiàn):

NSString *const BWWillUpdateListNotification = @"kWillUpdateListNotification";
const NSInteger MaxLeadCharCount = 44;
  1. 關(guān)于通知名稱,推薦使用Did/Will這樣的動詞連接表示最好;
  2. 如果導(dǎo)入的是UIKit類,就使用UIKIT_EXTERNFoundation類,就使用FOUNDATION_EXTERN;如果只在本類使用,只用寫實(shí)現(xiàn),不用寫聲明。

對于只在內(nèi)部聲明的const,需要添加static,這個(gè)我覺得可以不加,但是無法看到蘋果的實(shí)現(xiàn),不知道蘋果的規(guī)范怎么寫的。

2. 代碼格式

2.1 間距
a. 方法的大括號和其他的大括號(if/else/switch/while等等)應(yīng)始終和聲明在同一行開始,在新的一行結(jié)束;
b. 方法之間應(yīng)該正好空一行,這樣有助于視覺清晰度和代碼組織性。在方法中的功能塊之間也應(yīng)該使用空白行分開。
c. switch-case中,case后的代碼如果多于一行,則需要用{}包裹,建議所有casedefault后的代碼塊均用{}包裹。

建議的寫法:

if (user.isHappy) {
    // Do something cool
} else {
    // Do something else
}

不建議的寫法:

if (user.isHappy) 
{
    // Do something cool
} 
else 
{
    // Do something else
}

2.2 屬性關(guān)鍵字首個(gè)應(yīng)該是原子性,再到內(nèi)存管理關(guān)鍵詞。如果需要讀寫關(guān)鍵字的話,其排在第二位。
建議的寫法:

@property (nonatomic, readonly, copy) NSString *name;

2.3 推薦使用三元運(yùn)算符進(jìn)行運(yùn)算,它能使代碼更加簡潔、清晰。
當(dāng)三元運(yùn)算符的第二個(gè)參數(shù)(也就是if分支)返回的對象和條件語句中已經(jīng)檢查的對象一樣的時(shí)候,下面的表達(dá)方式更靈巧:
建議的寫法:

result = object ? : [self createObject];

不建議的寫法:

result = object ? object : [self createObject];

2.4 黃金大道規(guī)則(Golden Path
在使用條件語句編程時(shí),盡管會遇到邏輯復(fù)雜的代碼,我們也應(yīng)該盡量避免其嵌套導(dǎo)致閱讀困難。
盡量使用return將不符合邏輯的直接忽略掉,然后將要執(zhí)行的代碼放到判斷語句外面,減少嵌套。
建議的寫法:

- (void)someMethod {
    if (![someOther boolValue]) {
        return;
    }
    // Do something important
}

不建議的寫法:

- (void)someMethod {
    if ([someOther boolValue]) {
        // Do something important
    }
}

2.5 避免尤達(dá)表達(dá)式
不要使用尤達(dá)表達(dá)式,尤達(dá)表達(dá)式是指拿一個(gè)常量去和變量比較。
建議的寫法:

if ([myValue isEqual:@42]) { 
 
}

不建議的寫法:

if ([@42 isEqual:myValue]) {
 
} 

3. 文件引入方式

.h文件中盡量使用@class聲明文件,直到.m文件中真正需要的時(shí)候再使用#improt進(jìn)行引用,這樣能有效的防止相互引用、編譯失敗、不容易查找的bug等;這樣做的缺點(diǎn)是:.m文件還要#import其他類。
建議的寫法:

@class UIView, UIImage;

不建議的寫法:

@class UIView;
@class UIImage;

#improt頭文件順序:可以先引入系統(tǒng)文件,依次到Public.h,最后才到我們自己編寫的文件(可以大家一起商量一下順序)。記得檢查引用文件名稱,避免引入了沒有使用到的文件,發(fā)現(xiàn)后應(yīng)及時(shí)清除。
盡量按照系統(tǒng)類、第三方類、自己寫的類順序?qū)?,中間不能有空行。
寫法模板:
#import <系統(tǒng)庫>
#import <第三方庫>
#import "其他類"

建議的寫法:

#import <UIKit/UIKit.h>
#import <Google/Analytics.h>
#import "GBOrderEmptyView.h"

不建議的寫法:

#import "GBOrderEmptyView.h"

#import <UIKit/UIKit.h>

#import <Google/Analytics.h>

4. 不允許外界修改的屬性要設(shè)置 readonly

大家平時(shí)設(shè)置屬性默認(rèn)是可讀可寫的,但是這樣很容易對別人造成誤解,以為可以賦值。因此,對于只能獲取的屬性,一定寫readonly。

@property (nonatomic, readonly, copy) NSString *name;

5. BOOL 類型屬性的聲明

屬性set不要帶is,get要帶is。
建議的寫法:

@property (nonatomic, assign, getter=isUserLogin) BOOL userLogin;

不建議的寫法:

@property (nonatomic, assign) BOOL userLogin;

6. 對于初始化,一定要使用類對應(yīng)的初始化方法

a. UIView的對應(yīng)初始化方法為:- (instancetype)initWithFrame:(CGRect)initWithFrame

b. UIViewController的對應(yīng)初始化方法為:- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil

防止初始化用init、new等,沒經(jīng)過系統(tǒng)進(jìn)行設(shè)置一些默認(rèn)屬性而造成bug。

c. 對于局部變量,盡量進(jìn)行初始化。局部變量要初始化,屬性有默認(rèn)的值,所以我們不必須對屬性進(jìn)行初始化。
建議的寫法:

int index = 0;

不建議的寫法:

int index;

7. 對于一些狀態(tài)、選項(xiàng),使用枚舉,盡量少用數(shù)字、字符串判斷狀態(tài)。

建議的寫法:

typedef NS_ENUM(NSUInteger, HomeViewState) {
    HomeViewStateNoData,
    HomeViewStateFailure,
    HomeViewStateItemList,
    HomeViewStateBannerList,
};

if (state == HomeViewStateNoData) { // 顯示無數(shù)據(jù)
    
} else if (state == HomeViewStateFailure) { // 顯示請求錯(cuò)誤
    
} else if (state == HomeViewStateItemList) { // 顯示商品的列表
    
} else if (state == HomeViewStateBannerList) { // 顯示banner列表
    
}

不建議的寫法:

if (state == 0) { // 顯示無數(shù)據(jù)
    
} else if (state == 1) { // 顯示請求錯(cuò)誤
    
} else if (state == 2) { // 顯示商品的列表
    
} else if (state == 3) { // 顯示banner列表
    
}

8. 可變對象的使用

OC存在很多可變的對象,比如:NSMutableStringNSMutableArray、NSMutableDictionary等等,對于一些不允許改變的,直接使用不可變對象,可以節(jié)省對象開支,還可以防止別人修改數(shù)據(jù)造成bug。
建議的寫法:

NSArray *sexList = @[@"Man", @"Woman"];

不建議的寫法:

NSMutableArray *sexList = [NSMutableArray arrayWithArray:@[@"Man", @"Woman"]];

9. 遍歷的寫法

a. 如果只需要遍歷數(shù)組或字典,用forin
建議的寫法:

for (NSString *name in names) {
    // Do something very cool
}

不建議的寫法:

for (int i = 0; i < names.length, i++) {
    NSString *name = names[i];
}

b. 如果需要遍歷數(shù)組或字典的內(nèi)容,并且需要索引時(shí)使用enumerator。
建議的寫法:

[names enumerateObjectsUsingBlock:^(NSString * _Nonnull name, NSUInteger idx, BOOL * _Nonnull stop) {
    // Do something very cool
}];

不建議的寫法:

for (int i = 0; i < names.length, i++) {
    NSString *name = names[i];
}

10. 復(fù)雜的表達(dá)式

建議的寫法:

BOOL nameContainsSwift = [sessionName containsString:@"Swift"];
BOOL isCurrentYear     = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession    = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
    // Do something very cool
}

不建議的寫法:

if ([sessionName containsString:@"Swift"] && [sessionDateCompontents year] == 2014) {
    // Do something very cool
}

11. NSUserDefaults的代碼使用規(guī)范

因?yàn)橛玫?code>NSUserDefaults無非是保存和讀取數(shù)據(jù),所以先創(chuàng)建一個(gè)對象,可以精簡代碼,當(dāng)執(zhí)行方法很多,用變量替換。
對于我們?nèi)≈岛痛嬷档膋ey要定義一下,定義一下key方便我們使用,并且方便之后改名字。

建議的寫法:

NSString *user_id = @"user_id";
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"1" forKey:user_id];

不建議的寫法:

[[NSUserDefaults standardUserDefaults] setObject:@"1" forKey:@"user_id"];

12. 通知的移除

通知在dealloc要移除(記得在dealloc時(shí)釋放注冊的通知和 KVO 的監(jiān)聽)。
建議的寫法:

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

不建議的寫法:

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self name:name1 object:nil];
}

13. 創(chuàng)建單例的方法

正確的寫法:

+ (instancetype)sharedInstance {
    static BTSCustomView *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[BTSCustomView alloc] init];
    });
    return instance;
}

不建議的寫法:

+ (instancetype)sharedInstance {
    static BTSCustomView *instance = nil;
    if (!instance) {
        instance = [[BTSCustomView alloc] init];
    }
    return instance;
}

14. OC方法的代碼量

一個(gè)方法內(nèi)部盡量控制在最多50行,如果超過就精簡代碼,分開方法寫,方便之后進(jìn)行熱修復(fù)、代碼重構(gòu);注釋一定要寫,自己管理的類一定要注釋屬性用途、方法的用途、參數(shù)說明。
a> 屬性如果設(shè)置默認(rèn)值,一定要注明默認(rèn)值是什么;
b> 如果方法內(nèi)部存在邏輯判斷、方法跳轉(zhuǎn),一定注釋邏輯判斷的用途、方法跳轉(zhuǎn)的用途;
c> 除了初始化操作,其他聲明變量、賦值、判斷,應(yīng)該注釋用途。

15. #pragma mark的使用

對于屬性的不同作用,可以進(jìn)行分組,比如設(shè)置顏色的、設(shè)置字體的、設(shè)置其他樣式的;
對于方法的不同作用,可以進(jìn)行分類,比如添加功能、刪除功能的;
對于其他的代理方法、GetSet方法、Init初始化方法等,可以進(jìn)行分類。

建議的寫法:

#pragma mark - Init

#pragma mark - Request

#pragma mark - Delegate

#pragma mark - DataSource

#pragma mark - Setter

#pragma mark - Getter

16. 注釋的寫法

<1> 類注釋

/**
 訂單的cell
 */
@interface OrderCell : UITableViewCell

<2> 屬性注釋

/**
 當(dāng)前訂單cell
 */
@property (nonatomic, strong) OrderCell *cell;

<3> 方法注釋

/**
 顯示倒計(jì)時(shí)文本
 
 @param timerLabel 倒計(jì)時(shí)文本
 @param countTime 剩余時(shí)間
 @return 是否完成
 */
- (BOOL)timerLabel:(MZTimerLabel *)timerLabel finishedCountDownTimerWithTime:(NSTimeInterval)countTime;

<4> 局部變量和全局變量注釋

BOOL _isOfflinePay; // 是否是離線支付

<5> block注釋

/**
 驗(yàn)證輸入的是否正確
 
 @param inputText 輸入的文本
 @return 如果返回值存在就代表驗(yàn)證失?。环駝t,就代表成功
 */
typedef NSString *(^ATFVValidateInputCorrectComplete)(NSString *inputText);

<6> 枚舉(NS_ENUM) 注釋

/**
 當(dāng)前的狀態(tài)
 
 - HomeViewStateNoData: 顯示無數(shù)據(jù)
 - HomeViewStateFailure: 顯示請求錯(cuò)誤
 - HomeViewStateItemList: 顯示商品的列表
 - HomeViewStateBannerList: 顯示banner列表
 */
typedef NS_ENUM(NSUInteger, HomeViewState) {
    HomeViewStateNoData,
    HomeViewStateFailure,
    HomeViewStateItemList,
    HomeViewStateBannerList,
};

17. CGRect使用

當(dāng)需要訪問CGRect中某個(gè)成員變量時(shí),應(yīng)該使用CGGeometry函數(shù)來直接訪問,而不是使用.語法來獲取。
建議的寫法:

CGRect frame = self.view.frame;
 
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);

不建議的寫法:

CGRect frame = self.view.frame;
 
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;

18. 錯(cuò)誤處理

很多系統(tǒng)方法通過error返回指針的形式來表示錯(cuò)誤,我們應(yīng)該針對其返回值判斷,而非錯(cuò)誤變量。

建議的寫法:

NSError *error;
if (![self trySomethingWithError:&error]) {
    // 處理錯(cuò)誤
}

不建議的寫法:

NSError *error;
[self trySomethingWithError:&error];
if (error) {
    // 處理錯(cuò)誤
}

一些蘋果API在成功的情況下會寫一些垃圾值給錯(cuò)誤參數(shù)(如果非空),所以針對錯(cuò)誤變量可能會造成虛假結(jié)果以及接下來的崩潰。

其他

  1. 下面是整理的一些常用對仗詞,大家可以參考使用。
add/delete          添加/刪除        add/remove    添加/移除

begin/end           開始/結(jié)束

create/destroy      創(chuàng)建/銷毀

first/last          第一個(gè)/最后一個(gè)

get/release         獲取/釋放        get/set       取出/設(shè)置

increment/decrement 增加/減少        insert/delete 插入/刪除

lock/unlock         鎖/解鎖

next/previous       下一個(gè)/前一個(gè)

old/new             舊的/新的        open/close    打開/關(guān)閉

pop/push            出棧/入棧        put/get       放入/取出

send/receive        發(fā)送/接收        show/hide     顯示/隱藏         source/target  源/目標(biāo)
source/sink         來源/接收器      source/destination 源/目的地     start/stop      開始/停止
store/query         存儲/查詢

up/down             向上/向下

visible/invisible   可見/不可見



settings  配置
traversal  遍歷
Proactor  設(shè)計(jì)模式
adapter  適配器
listener  監(jiān)聽器
trigger  觸發(fā)器
acceptor 接收器
connector  連接器
dispatch  調(diào)度/分派/分發(fā)
dispatcher  分派器
reactor  反應(yīng)器
executor  執(zhí)行器
parser  解析器
builder  生成器/構(gòu)造器
handle  句柄/處理
handler  處理器
invoke  調(diào)用
invoker  調(diào)用方
masterplate  模板
  1. iPhone屏幕適配比例
//    屏幕寬高                  寬度                         高度
3.5   320x480      100 / 414 * 320 ~= 77.3     100 / 736 * 480 ~= 65.2
4.0   320x568      100 / 414 * 320 ~= 77.3     100 / 736 * 568 ~= 77.2
4.7   375x667      100 / 414 * 375 ~= 90.6     100 / 736 * 667 ~= 90.6
5.5   414x736      100 / 414 * 414 ~= 100      100 / 736 * 736 ~= 100
5.8   375x812      100 / 414 * 375 ~= 90.6     100 / 736 * 812 ~= 110.3


iPhone 6 和 iPhone X 屏幕尺寸對比:

          iPhone 6             iPhone X

          375x667               375x812
        ************         ************
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        *          *         *          *
        ************         *          *
                             *          *
                             *          *
                             ************
寬度相同,高度不同:

667 = 20 + 44 + 554 + 49

812 = 44 + 44 + 641 + 49 + 34


Device Screen Sizes

設(shè)備 屏幕尺寸 (英寸) 點(diǎn)分辨率 (pt) 像素分辨率 (px) PPI (DPI) 渲染后
iPhone 3GS 3.5 320 x 480 320 x 480 163
iPhone 4 / 4s 3.5 320 x 480 640 x 960 326
iPhone 5 / 5s / SE 4.0 320 x 568 640 x 1136 326
iPhone 6 / 6s / 7 / 8 4.7 375 x 667 750 x 1334 326
iPhone 6 Plus / 6s Plus / 7 Plus / 8 Plus 5.5 414 x 736 1242 x 2208 401 1080 x 1920
iPhone X / Xs / 11 Pro 5.8 375 x 812 1125 x 2436 458
iPhone XR / 11 6.1 414 x 896 828 x 1792 326
iPhone Xs Max / 11 Pro Max 6.5 414 x 896 1242 x 2688 458
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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