Objective-C 代碼規(guī)范

版本 內(nèi)容 修訂人 時(shí)間
0.1.0 草稿 黃鑫 2018/06/11
0.2.0 修改文檔組織 黃鑫 2018/06/16

0. 前言


"代碼是寫給人看的"

例子??

//
//  M2User.h
//  M2API
//
//  Created by Kim on 2018/06/11.
//

// 頭文件引入
#import <Foundation.h>

#import "M2Defines.h"


// 常量定義
FOUNDATION_EXPORT NSString * const M2UserErrorDomain;

/**
  性別枚舉 
*/
typedef NS_ENUM(NSUInteger, M2Gender) {
    M2GenderUnknow = 0,     //!< 未知
    M2GenderMale,           //!< 男性
    M2GenderFemale          //!< 女性
};


/** 用戶  */
@interface M2User : NSObject

@property (nonatomic, readonly, copy) NSString *name;       //!< 名字
@property (nonatomic, readonly, assign) NSUInteger age;     //!< 年齡
@property (nonatomic, readonly, assign) M2Gender gender;    //!< 性別

/**
  初始化
  
  @param name 用戶名
  @param age 年齡
  @param gender 性別  
*/
+ (instancetype)userWithName:(NSString * __Nonnull)name 
                         age:(NSUInteger)age 
                      gender:(M2Gender)gender;

- (instancetype)initWithName:(NSString * __Nonnull)name
                         age:(NSUInteger)age
                      gender:(M2Gender)gender;

@end

// 實(shí)現(xiàn) 

@implementation M2User

+ (instancetype)userWithName:(NSString * __Nonnull)name 
                         age:(NSUInteger)age 
                      gender:(M2Gender)gender {
    return [[self alloc] initWithName:name age:age gender:gender];                               
}

- (instancetype)initWithName:(NSString * __Nonnull)name 
                         age:(NSUInteger)age 
                      gender:(M2Gender)gender {
    if (self = [super init]) {
        _name = name;
        _age = age;
        _gender = gender;
    }
    
    return self;
}

@end

1. 布局與風(fēng)格


良好布局的目的

  • 準(zhǔn)確表現(xiàn)代碼的邏輯結(jié)構(gòu)
  • 始終如一地表現(xiàn)代碼的邏輯結(jié)構(gòu)
  • 改善可讀性
  • 經(jīng)得起修改

布局技術(shù)

  • 分組 從另一個(gè)角度看,空白也是分組,也是確保相關(guān)到語(yǔ)句組成放在一起。
  • 空行 是指示一個(gè)程序如何組織的手段。可以用空行將相關(guān)語(yǔ)句各自劃分成段落,分開各個(gè)子程序,突出注釋部分。
  • 縮進(jìn) 使用縮進(jìn)形式顯示程序的邏輯結(jié)構(gòu)

“當(dāng)程序有兩到四個(gè)空格的縮進(jìn)時(shí),受試者對(duì)程序的理解分?jǐn)?shù)會(huì)比毫無縮進(jìn)的程序高出20%到30%?!?/p>

— 《程序縮進(jìn)和可理解性》

2. 代碼組織


Objective-C的類通常分成頭文件和實(shí)現(xiàn)文件。

頭文件

頭文件通常包含:

  • 文件說明與版權(quán)
  • 頭文件引入
  • 宏定義
  • 常量定義
  • 類型前置聲明
  • 塊類型定義
  • 枚舉定義
  • 函數(shù)定義
  • 協(xié)議定義
  • 類定義 - 類定義通常包含
    • 類方法。
    • 屬性。
    • 公開方法。
  • 分類定義
    • 分類方法。

?? 注意:內(nèi)容排列順序與上面一致。

如下面的頭文件模板所示。按照下面的順序定義。

// 文件說明與版權(quán)
//
//  M2API2Client.h
//  M2API
//
//  Created by Kim on 2017/11/11.
//  Copyright (c) 2017 Kim Studio. All rights reserved.
//

// 頭文件引入 (見下文說明)
#import <Foundation.h>

#import "M2APIClient.h"


// 宏定義 (見下文說明。必須是才使用宏)
#define M2_DEBUG  0
#define M2_TEST   1

// 常量定義
FOUNDATION_EXPORT NSString * const M2UserErrorDomain;

// 類型前置聲明
@class User;

// 類型定義
typedef NSString * const M2APIHTTPMethod;

// Block類型定義
typedef void (^M2APISuccessBlock)(id response);
typedef void (^M2APIFailureBlock)(NSError *error);

// 枚舉定義
typedef NS_ENUM(NSUInteger, M2DirectionType) {
    M2DirectionTypeUnknown = 0,
    M2DirectionTypeTop,
    M2DirectionTypeLeft,
    M2DirectionTypeButtom,
    M2DirectionTypeRight
};

// 協(xié)議定義
@protocol M2LoginViewDelegate : NSObject

@end

// 類定義
@interface M2User : NSObject

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

@end
    
// 分類定義
@interface M2User <M2Debug>

- (NSString *)debugInfo;
    
@end

?? 通常使用文件模板

版權(quán)

文件頭中增加版權(quán)信息。

//
//  M2API2Client.h
//  M2API
//
//  Created by Kim on 2017/11/11.
//  Copyright (c) 2017 Kim Studio. All rights reserved.
//

頭文件引入

頭文件引入規(guī)則順序:

  • 系統(tǒng)庫(kù)
  • 第三方庫(kù)
  • 工程內(nèi)類引入

注意:

  1. 系統(tǒng)庫(kù)/第三方庫(kù)與工程內(nèi)類引入直接的分組空行。
  2. 工程內(nèi)類如果有多個(gè)頭文件引入,也可以增加空行按功能進(jìn)行分組。
//
//  M2API2Client.h
//  M2API
//
//  Created by Kim on 2017/11/11.
//  Copyright (c) 2017 Kim Studio. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AFNetworking/AFNetworking.h>
#import <CocoaLumberjack/CocoaLumberjack.h>
#import <AFHTTPSessionManagerLogger.h>

#import "M2APIConfiguration.h"
#import "M2APIHTTPSessionManager.h"

實(shí)現(xiàn)文件

實(shí)現(xiàn)文件通常包含:

// 文件描述

// 頭文件引入

// 常量定義

// 文件內(nèi)私有類定義

// 類私有方法定義

// 類實(shí)現(xiàn)

類定義

一般類定義組成如下:

  • 協(xié)議
    • 成員變量
    • 屬性
    • 類方法
    • 構(gòu)造函數(shù)
    • 公開方法
    • 控件響應(yīng)函數(shù)
    • 通知響應(yīng)函數(shù)
    • 委托方法
    • 私有方法

空行與注釋:

  1. 協(xié)議之間留2行空行。
  2. @protocol/@interface第一個(gè)屬性方法后,空1行
  3. 最后一個(gè)方法@end之間空1行。
  4. 類最后空1行。
  5. 屬性第一個(gè)方法之間空1行。
  6. 屬性如果有長(zhǎng)注釋,則空1行。
  7. 屬性如果使用短注釋,則在屬性后使用//!<進(jìn)行注釋。
  8. 如果方法定義有注釋,則空1行。
  9. 如果方法定義沒有注釋,則可以不留空行進(jìn)行分組。
  10. 方法分組之間,空1行。
  11. 函數(shù)定義的注意點(diǎn),見后面函數(shù)一節(jié)
////// 頭文件說明
//
//  M2APIUserClient.h
//  M2UserAPI
//  Created by Kim on 2018/06/10
//  Copyright (c) 2017 Kim Studio. All rights reserved.

////// 頭文件引入
#import <M2HTTPClient.h>
#import "M2APIUser.h"

////// 協(xié)議定義
@protocol M2APIUserEndPoint <NSObject>

/**
  登陸
  
  @param user 用戶名
  @param password 密碼
  
  @return 信號(hào) M2APIUser
 */
- (RACSignal *)loginWithUser:(NSString *)user password:(NSString *)password;

/**
  獲取用戶信息
  
  @return 信號(hào) 用戶信息
 */
- (RACSignal *)userInfo;

@end

/////// 類定義
/**
  用戶模塊客戶端
 */
@interface M2APIUserClient : M2HTTPClient <M2APIUserEndPoint>

@property (nonatomic, strong) M2APIConfiguration *configuration; //!< 配置消息
@property (nonatomic, strong) M2APISigner *signer; //!< 簽名類

+ (instancetype)sharedClient;
+ (instancetype)clientWithConfiguration:(M2APIConfiguration *)configuration;
- (instancetype)initWithConfiguration:(M2APIConfiguration *)configuration;

@end

類實(shí)現(xiàn)

類實(shí)現(xiàn)內(nèi)部組織 使用#pargma mark -來分割功能組。一個(gè)典型的ViewController的實(shí)現(xiàn)功能分組有:

  • 類方法。

    • 單件函數(shù)。
    • 其他類方法。
  • Lifecycle。 對(duì)象生命周期函數(shù)

    • 對(duì)象生命周期函數(shù)。init, dealloc, descrition
  • 自定義屬性。

  • UI對(duì)象的事件響應(yīng)函數(shù)。

  • 公開方法。

  • 私有方法和輔助函數(shù)。

  • 通知處理函數(shù)。

  • 委托方法。

#pargma mark - Class methods
+ (instancetype)sharedInstance {}
+ (CGFloat)viewHeightForObject:(id)object {}

#pargma mark - Lifecycle
- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pargma mark - Custom Accessors
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pargma mark - IBActions
- (IBAction)onSubmitDataAction:(id)sender {}

#pargma mark - Public 
- (void)publicMethod {}

#pargma mark - Private helpers or utils
- (void)m2_privateMethod {}

#pargma mark - Notification Handlers
- (void)onEnterBackgroundHandler:(NSNotification *)notification {}

#pargma mark - Delegate methods
// 多個(gè)delegate 進(jìn)行分組

?? 使用文件模板

3. 命名


Apple命名規(guī)則盡可能堅(jiān)持,特別是與這些相關(guān)的memory management rules(NARC)。

長(zhǎng)的,描述性的方法和變量命名是好的。

命名涉及到比較多:

  • 庫(kù)名
  • 文件名
  • 類名
  • 函數(shù)名
  • 變量名

庫(kù)名

設(shè)計(jì)一個(gè)庫(kù)通常使用前綴+名稱的方式。eg.

  • UIKit
  • AVFoundation
  • SDWebImage
  • AFNetworking

文件名

文件名命名規(guī)則與類命名規(guī)則一致:

  • 命名空間。本項(xiàng)目/或項(xiàng)目模塊縮略前綴。eg. M2API, M2BL, M2PL。
  • 功能名詞。User,Device,F(xiàn)ile,VideoPlayer。
  • 功能分類名字。例如,
    • Client 代表DAL的網(wǎng)絡(luò)訪問客戶端。
    • Data代表DAL的DTO (Data Transfer Object)。
    • 使用名詞作為領(lǐng)域模型名稱。
    • Service代表BL的業(yè)務(wù)邏輯類。
    • Item代表PL中View的VO(View Object)。
    • View代表PL的視圖類。
    • Controller代表PL的中MVC模式的C控制器
    • ViewModel/Store代表PLMVVMVM或者M(jìn)VCS的S。

eg.

  • UIViewController.h
  • M2PLLoginView.h

命名空間

由于Objective-C 沒有命名空間,所以通常使用項(xiàng)目名的頭字母用于:

  • 常量

  • 枚舉

  • C函數(shù)名

  • 全局變量名

  • 類名

  • 塊類型名

前綴應(yīng)由不少于3個(gè)字母組成(蘋果保留所有2個(gè)字母的前綴)。可以是APP名、公司名縮寫等。

例子??

// 宏
#debug M2_DEBUG

// 常量
FOUNDATION_EXPORT NSString * const M2UserErrorDomain;

// 別名
typedef NSString * const M2HTTPMethod;

// 塊類型名
typedef void (^M2APISuccessBlock)(id response);
typedef void (^M2APIFailureBlock)(NSError *error);

// 類名
@class M2User;

盡量少使用宏來定義常量。宏通常用于編譯條件。

  • 增加命名空間。
  • 單詞使用大寫。
  • 使用下劃線連接單詞。

使用

#define M2_DEBUG

不使用

#define Production
#define M2_Production

常量

常量通常使用與字符串類型常量與值類型常量。注意點(diǎn):

  • 命名空間。命名空間前綴全部大寫
  • 使用駝峰命名
// 頭文件 .h
FOUNDATION_EXPORT NSString * const M2UserErrorDomain;
FOUNDATION const CGFloat M2UserMaxAge;

// 實(shí)現(xiàn)文件 .m
static NSString * const M2UserError = @"net.kim.M2UserErrorDomain"; // 跨文件使用
static const CGFloat M2UserMaxAge = 200; // 跨文件使用

static const CGFloat M2UserMinAge = 0; // 文件內(nèi)部使用

塊類型

塊類型定義。注意點(diǎn):

  • 命名。命名空間 + 功能名詞 + Block
    • 命名空間。見《命名空間一節(jié)》
    • 功能名詞。
    • 后綴Block。以區(qū)別其他類型。
  • 注意返回類型后需要增加一個(gè)空格。

typedef void (^M2APISuccessBlock)(id response);
typedef void (^M2APIFailureBlock)(NSError *error);

4. 類


  • 類方法
  • 單例模式
  • 屬性
  • 實(shí)例方法

類方法

單例模式

單例對(duì)象應(yīng)該使用線程安全模式來創(chuàng)建共享實(shí)例。

+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

這會(huì)防止possible and sometimes prolific crashes.

屬性

公開屬性

屬性特性排列順序如下:

  • 是否原子atomic/nonatomic。雖然默認(rèn)為atomic, 原子訪問時(shí)還是需要顯式說明。
  • 讀寫readonly/readwrite。默認(rèn)為readwrite。只讀是需要顯式說明。
  • 訪問器getter/setter。如果是布爾類型getter,需要加is前綴。
  • 存儲(chǔ)特性weak/strong/copy/assign。放在最后。

??Tip 不可變性 Immutable

為了避免數(shù)據(jù)遭到不必要的修改:

  1. 不應(yīng)該被外部直接修改的屬性,應(yīng)該聲明(readonly)(可以在Extension中重新聲明為(readwrite),使它對(duì)外只讀,對(duì)內(nèi)可讀寫)。
  2. 不要把NSMutable(Array/Dictionary/Set...)暴露出來,應(yīng)該只留一個(gè)setter給外部使用,以免它們被其他類修改時(shí),類自身難以察覺。
  3. 如果數(shù)據(jù)不是特別多,copy的代價(jià)不是特別大,留給其他類的getter應(yīng)盡量用copy方法,返回一個(gè)不可變的對(duì)象。

例子

@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) M2Gender gender;
@property (nonatomic, readonly, getter=isLogin, assign) BOOL login;

??Tip 控件屬性命名

  • 控件功能名詞 + 后綴不帶命名空間的控件類型名

例子

@interface M2PLoginView : UIView

@property (nonatomic, weak) IBOutlet UILabel *userLabel; //!< 用戶名標(biāo)簽
@property (nonatomic, weak) IBOutlet UITextField *userTextField; //!< 用戶名輸入

@end

私有屬性

如果私有屬性在模塊內(nèi)部可以訪問,則使用私有頭文件。

eg. M2User_Private.h 在有需要使用到的實(shí)現(xiàn)文件引入即可。

?? 注意:私有頭文件,在模塊外部不可訪問。生成庫(kù)時(shí)需要注意忽略改頭文件。

例子

/// 實(shí)現(xiàn)文件

@interface M2User ()

@property (nonatomic, copy)NSString *name;
@property (nonatomic, assign)NSUInteger age;
@property (nonatomic, assign)M2Gender gender;

@end
    
@implementation M2User
    
// ...
    
@end

自定義屬性

下面是一個(gè)懶加載的自定義屬性:

- (NSMultableDictionary *)extraInfo {
    if (!_extraInfo) {
        _extraInfo = [NSMutableDictionary dictionary];
    }
    
    return _extraInfo
}

自定義屬性設(shè)置

- (void)setExtraInfo:(NSDictionary *)extraInfo {
    _extraInfo = extraInfo;
    // Do something else.
    // 其他副作用。
}

?? 注意:在自定義屬性內(nèi)增加副作用需要特別注意。需要在屬性增加注釋說明。

成員變量

私有屬性,一般定義在類的實(shí)現(xiàn)文件。

/// 實(shí)現(xiàn)文件

@interface M2User () {
    BOOL _status;
}

@end
    
@implementation M2User
    
// ...
    
@end

標(biāo)識(shí)位

@interface Fool () {
    struct {
        BOOL step1Done;
        BOOL step2Done;
    } _flags;
}


// 使用
_flag.step1Done = YES;
_flag.step1Done = NO;

類初始化方法

@interface Airplan

+ (instancetype)airplanWithType:(AirplanType)type;

@end
    
@implementation Airplan

關(guān)于更多instancetype信息,請(qǐng)查看NSHipster.com

Init方法

Init方法應(yīng)該遵循Apple生成代碼模板的命名規(guī)則。返回類型應(yīng)該使用instancetype而不是id

- (instancetype)init {
    if (self = [super init]) {
        // ...
    }
    
    return self;
}

// 或者
- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        // ...
    }
    
    return self;
}

查看關(guān)于instancetype的文章Class Constructor Methods

方法定義

響應(yīng)函數(shù)

規(guī)則:

  • 控件響應(yīng)函數(shù):前綴on + 功能動(dòng)作 + 后綴Action。
  • 通知響應(yīng)函數(shù):前綴on + 通知 + 后綴Handler

使用

// 控件響應(yīng)函數(shù)
- (IBAction)onLoginAction:(id)sender {
    // ...
}

// 通知響應(yīng)函數(shù)
- (void)onEnterBackgroundHandler:(NSNotification *)notification {
    // ...
}

不使用

- (IBAction)login:(id)sender {
    // ...
}

- (void)enterBackground:(NSNotification *)noti {
    // ...
}

分類

@interface M2APIUserClient (User)

// 登陸
- (RACSignal *)loginWithUser:(NSString *)user password:(NSString *)password;

// 登出
- (RACSignal *)logout;

@end

5. 子程序


變量[TODO]

布爾值

Objective-C使用YESNO。因?yàn)?code>true和false應(yīng)該只在CoreFoundation,C或C++代碼使用。既然nil解析成NO,所以沒有必要在條件語(yǔ)句比較。不要拿某樣?xùn)|西直接與YES比較,因?yàn)?code>YES被定義為1和一個(gè)BOOL能被設(shè)置為8位。

這是為了在不同文件保持一致性和在視覺上更加簡(jiǎn)潔而考慮。

使用

if (someObject) {}
if (![anotherObject boolValue]) {}

不使用

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.

如果BOOL屬性的名字是一個(gè)形容詞,屬性就能忽略"is"前綴,但要指定get訪問器的慣用名稱。例如:

@property (assign, getter=isEditable) BOOL editable;

文字和例子從這里引用Cocoa Naming Guidelines

條件語(yǔ)句if/else

條件語(yǔ)句主體為了防止出錯(cuò)應(yīng)該使用大括號(hào)包圍,即使條件語(yǔ)句主體能夠不用大括號(hào)編寫(如,只用一行代碼)。這些錯(cuò)誤包括添加第二行代碼和期望它成為if語(yǔ)句;還有,even more dangerous defect可能發(fā)生在if語(yǔ)句里面一行代碼被注釋了,然后下一行代碼不知不覺地成為if語(yǔ)句的一部分。除此之外,這種風(fēng)格與其他條件語(yǔ)句的風(fēng)格保持一致,所以更加容易閱讀。

使用

// Good!
if (!error) {
    return success;
}

不使用

// Bad
// 沒有花括號(hào),容易多些空行,造成邏輯提前返回。
if (!error)
    return success;

if (!error) return success;

if (error != nil)
    return success

多條件情而且單行過長(zhǎng)的情況下,使用換行。條件符放行最后。

使用

// Good! 
if (direction == M2Left ||
    direction == M2Right) {
    // ...
} 

不使用

// Bad! 
if (direction == M2Left 
    || direction == M2Right) {
    // ...
} 

使用

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

不使用

if (user.isHappy)
{
    // ...
}
else {
    // ...
}

Switch-Case

- (void)handleMessage:(M2Message *)message {
    
    // 注意花括號(hào)與break。
    M2MessageType type = message.type;
    switch(type) {
        case M2MessageChat: {
            // ...
        } break;
        
        case M2MessageNotify: {
            // ...
        } break;
        case M2MessageSystem: {
            // ...
        } break;
            
        default:
           break;
    }
}

三元操作符 ?:

當(dāng)需要提高代碼的清晰性和簡(jiǎn)潔性時(shí),三元操作符?:才會(huì)使用。單個(gè)條件求值常常需要它。多個(gè)條件求值時(shí),如果使用if語(yǔ)句或重構(gòu)成實(shí)例變量時(shí),代碼會(huì)更加易讀。一般來說,最好使用三元操作符是在根據(jù)條件來賦值的情況下。

Non-boolean的變量與某東西比較,加上括號(hào)()會(huì)提高可讀性。如果被比較的變量是boolean類型,那么就不需要括號(hào)。

使用

NSInteger value = 5;
result = (value != 0) ? x : y;

Bool isHorizontal = YES;
result = isHorizontal ? x : y;

NSString *name = nil;
result = name ?: @"";

不使用

BOOL result = value!=0 ?x:y;

block

使用

typedef void (^M2SuccessBlock)(id response);

M2SuccessBlock successBlock = ^(id response) {
    // Do something.
};

不使用

// 可讀性不夠強(qiáng),另外可能導(dǎo)致過長(zhǎng)行。
void (^successBlock)(id response) = ^(id response) {
    // Do something.
};

使用

// 風(fēng)格1
[userClient loginWithUser:user password:password success:^(id user) {
    // do something. 
} failure:^(NSError *error) {
    // ...
}];


// *******************************************
// 如果success/failure塊過長(zhǎng),則可以前綴定義塊。
// 風(fēng)格2
M2APISuccessBlock successBlock = ^(id response) {
  // do something.  
};

M2APIFailureBlock failureBlok = ^(NSError *error) {
  // ...  
};

// 單行過長(zhǎng),則換行。
[userClient loginWithUser:user 
                 password:password 
                  success:successBlock 
                  failure:failureBlock];

字面值

使用

// 數(shù)組
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];

// 字典
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};

// Number
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

不使用

// 數(shù)組
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];

// 字典
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];

// Number
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];

數(shù)組

// 數(shù)組
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];

names[]

字典

// 字典創(chuàng)建
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};

// 訪問
NSString *product = productManagers[@"iPhone"];

// 
NSMutableDictionary *user = [NSMutableDictionary dictionary];

// 設(shè)置
user[@"name"] = @"Bob";
user[@"title"] = @"IT Manager";
user[@"age"] = @25;

// 迭代
[user enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
   NSLog(@"key : %@, value : %@", key, obj);     
}];

CGRect 函數(shù)

使用

// Good!
CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

不使用

// Bad
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;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

注釋

當(dāng)需要注釋時(shí),注釋應(yīng)該用來解釋這段特殊代碼為什么要這樣做。任何被使用的注釋都必須保持最新或被刪除。

一般都避免使用塊注釋,因?yàn)榇a盡可能做到自解釋,只有當(dāng)斷斷續(xù)續(xù)或幾行代碼時(shí)才需要注釋。例外:這不應(yīng)用在生成文檔的注釋

空格

空行

6. 資源


iOS應(yīng)用包含多種資源文件

  • storyboard/xib
  • 圖片
  • 字符串
  • 字體
  • 多媒體

storyboard/xib

圖片

字符串

字體

多媒體

7. 模塊


8. Xcode工程


物理文件應(yīng)該與Xcode工程文件保持同步來避免文件擴(kuò)張。任何Xcode分組的創(chuàng)建應(yīng)該在文件系統(tǒng)的文件體現(xiàn)。代碼不僅是根據(jù)類型來分組,而且還可以根據(jù)功能來分組,這樣代碼更加清晰。

盡可能在target的Build Settings打開"Treat Warnings as Errors,和啟用以下additional warnings。如果你需要忽略特殊的警告,使用 Clang's pragma feature

9. 輔助工具


  • Spacecommander

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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