在日常的開發過程中,如果大家都遵循統一的代碼規范,我們就可以避免許多無緣無故的Bug,提高程序的準確性、連續性、可讀性、可維護性更重要的是,統一的程序布局和編程風格,有助于提高整個項目的開發質量,提高開發效率,降低開發成本。同時,對于普通程序員來說,養成良好的編程習慣有助于提高自己的編程水平,提高編程效率。因此,統一的、良好的程序布局和編程風格不僅僅是個人主觀美學上的或是形式上的問題,而且會涉及到產品質量,涉及到個人編程能力的提高,必須引起大家重視。。以下是我們公司一些常見的代碼規范以及個人認為一些比較好的代碼風格來和大家分享一下:
1. .h和.m文件布局
1.1 .h文件布局,請遵循以下順序來擼代碼
文件頭(該文件信息的描述)
#import(依次為標準庫頭文件、非標準庫頭文件)
文件內部使用的宏定義
常量定義 (文件內部使用的數據類型,比如枚舉等)
靜態全局變量定義
代理協議定義(如果有使用了代理等)
類的實現
成員變量
類方法
實例方法
1.2 .m文件布局
#pragma mark – init
#pragma mark - View lifecycle
#pragma mark – Notification
#pragma mark – Delegate
#pragma mark - 按鈕事件
#pragma mark – 自定義方法
#pragma mark – get set方法
2. 項目目錄文件分類
2.1 每個功能塊放入一個Group,在目錄里建立實際文件夾管理,不能虛擬目錄。公司采用的是MVVM設計模式,每個功能模塊應包括以下幾個部分
View
ViewControlle
Model
ViewModel
2.2 建立Library文件夾,所有第三方庫放入其中
2.3 建立Common文件夾,所有模塊公用的放入其中
2.4 建立Resources文件夾,資源文件放入其中
2.5 建立SRC文件夾,源代碼文件放入其中
3. 引入其它類時,若要作為實例變量的在.h中引入。否則在.m中引入。
a:聲明實例變量一律以屬性聲明。
b:其它類要訪問的實例變量和方法在.h文件中聲明,否則聲明于.m文件中。
c:實例變量及方法以功能塊放在一起,實現一個功能的連續著放在一起,另一個功能的空一行開始聲明。
4. 建議使用“#pragma mark”,方便閱讀代碼
5. 界面初始化和布局如果用code實現,需要寫到單獨方法里面。不要把過多邏輯寫在viewDidLoad
說明:這樣實現比較清晰。
正例
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.viewModel=LoginViewModel.new;
self.title=@"登錄";
[self initViews];
[self autolayoutViews];
[self bindViews];
};
-(void)initViews
{
[self.view addSubview:self.usernameTextField];
[self.view addSubview:self.passwordTextField];
[self.view addSubview:self.loginButton];
[self.view addSubview:self.finpwdButton];
[self.view addSubview:self.registerButton];
}
反例
-(void) viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UITextField *usernameTextField = UITextField.new;
usernameTextField.backgroundColor = [UIColor clearColor];
usernameTextField.delegate=self;
usernameTextField.placeholder=@"請輸入手機";
usernameTextField.font=[UIFont systemFontOfSize:16];
[self.view addSubview:self.usernameTextField];
}
6. 代碼基本格式
6.1 if、else、else if、for、while、do等語句自占一行,執行語句不得緊跟其后。不論執行語句有多少都要加 { }。
說明:這樣可以防止書寫失誤,也易于閱讀。
正例
if (varible1 < varible2)
{
varible1 = varible2;
}
反例:下面的代碼執行語句緊跟if的條件之后,而且沒有加{},違反規則。
if (varible1 < varible2) varible1 = varible2;
6.2 { 和 }獨占一行
說明:這樣可以防止書寫失誤,也易于閱讀。
正例:
if (varible1 < varible2)
{
varible1 = varible2;
}
反例:
if (varible1 < varible2) {
varible1 = varible2;
}
6.3 定義指針類型的變量,*應放在變量前。
正例:
float *pfBuffer;
反例:
float* pfBuffer;
6.4 當參數過長時,每個參數占用一行,以冒號對齊。
- (void)writeFisrtNumber:(NSString *)firstStr
withNextNumber:(NSString *)nextStr
withLastNumber:(NSString *)lastStr
{
}
6.5 程序的分界符‘{’和‘}’應獨占一行并且位于同一列,同時與引用它們的語句左對齊。{ }之內的代碼塊使用縮進規則對齊。
6.6 注釋符與注釋內容之間要用一個空格進行分隔。
6.7 方法名與形參不能留空格,返回類型與方法標識符有一個空格。
6.8 關鍵字之后要留空格,函數名之后不要留空格。
6.9 聲明類或方法時,注意空格的使用,參數過多時可換行保持對齊。
6.10 長表達式要在低優先級操作符處拆分成新行,操作符放在新行之首,拆分出的新行要進行適當的縮進,使排版整齊。
說明:條件表達式的續行在第一個條件處對齊。
for循環語句的續行在初始化條件語句處對齊。
函數調用和函數聲明的續行在第一個參數處對齊。
賦值語句的續行應在賦值號處對齊。
正例:
if ((iFormat == CH_A_Format_M)
&& (iOfficeType == CH_BSC_M)) // 條件表達式的續行在第一個條件處對齊
{
doSomething();
}
for (long_initialization_statement;
long_condiction_statement; // for循環語句續行在初始化條件語句處對齊
long_update_statement)
{
doSomething();
}
// 函數聲明的續行在第一個參數處對齊
BYTE ReportStatusCheckPara(HWND hWnd,
BYTE ucCallNo,
BYTE ucStatusReportNo);
// 賦值語句的續行應在賦值號處對齊
fTotalBill = fTotalBill + faCustomerPurchases[iID]
+ fSalesTax(faCustomerPurchases[iID]);
6.11 函數(方法)聲明時,類型與名稱不允許分行書寫
正例:
extern double FAR CalcArea(double dWidth, double dHeight);
反例:
extern double FAR CalcArea(double dWidth, double dHeight)
;
6.12 建議換行規則
A) .h中的空行
1、文件說明與頭文件包含(#import)之間空1行
2、頭文件包含(#import)之間,如果需要分類區別,各類別之間空1行。
3、頭文件包含(#import)與@class之間空1行。
4、@interface與@class之間空1行。
5、頭文件{}里面,空1行開始聲明對象成員,如果需要分類區別,各類別之間空1行。
6、頭文件{}外,空1行書寫屬性,如果需要分類區別,各類別之間空1行。
7、屬性下面空1行開始寫方法,如果需要分類區別,各類別之間空1行。
8、方法完成后,空1行@end。
9、如果需要聲明protocol,空2行接著寫。通常protocol寫在@end后面,但是聲明在@interface之前。
B) .m中的空行
1、文件說明與頭文件包含(#import)之間空1行
2、頭文件包含(#import)之間,如果需要分類區別,各類別之間空1行。
3、@implementation和@synthesize之間空1行, 如果需要分類區別,各類別之間空1行。
4、@synthesize與方法之間空1行。
5、方法與方法之間空1行。
C) 方法里面的空行
1、變量聲明后需要空1行,如果需要分類區別,各類別之間空1行。
2、條件、循環,選擇語句,整個語句結束,需要空1行。
3、各功能快之間空1行。
4、最后一個括弧之前不空行。
5、注釋與代碼之間不空行。
6、#pragma mark 與方法之間空1行。
7. 注釋
7.1 多行注釋采用“/* … */”,單行注釋采用“// …”
7.2 一般情況下,源程序有效注釋量必須在30%以上。
說明:注釋的原則是有助于對程序的閱讀理解,注釋不宜太多也不能太少,注釋語言必須準確、易懂、簡潔。有效的注釋是指在代碼的功能、意圖層次上進行注釋,提供有用、額外的信息。
7.3 注釋應與其描述的代碼相近,對代碼的注釋應放在其上方或右方(對單條語句的注釋)相鄰位置,不可放在下面,如放于上方則需與其上面的代碼用空行隔開。
說明:在使用縮寫時或之前,應對縮寫進行必要的說明。
正例:
如下書寫比較結構清晰
/* 獲得子系統索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
/* 代碼段1注釋 */
[ 代碼段1 ]
/* 代碼段2注釋 */
[ 代碼段2 ]
反例1:
如下例子注釋與描述的代碼相隔太遠。
/* 獲得子系統索引 */
iSubSysIndex = aData[iIndex].iSysIndex;
反例2:
如下例子注釋不應放在所描述的代碼下面。
iSubSysIndex = aData[iIndex].iSysIndex;
/* 獲得子系統索引 */
反例3:
如下例子,顯得代碼與注釋過于緊湊。
/* 代碼段1注釋 */
[ 代碼段1 ]
/* 代碼段2注釋 */
[ 代碼段2 ]
7.4 全局變量要有詳細的注釋,包括對其功能、取值范圍、訪問信息及訪問時注意事項等的說明。
正例:
/*
* 變量作用說明
* 變量值說明
*/
BYTE g_ucTranErrorCode;
7.5 注釋與所描述內容進行同樣的縮排。
說明:可使程序排版整齊,并方便注釋的閱讀與理解。
正例:
如下注釋結構比較清晰
- (int)doSomething
{
/* 代碼段1注釋 */
[ 代碼段1 ]
/* 代碼段2注釋 */
[ 代碼段2 ]
}
反例:
如下例子,排版不整齊,閱讀不方便;
int DoSomething(void)
{
/* 代碼段1注釋 */
[ 代碼段1 ]
/* 代碼段2注釋 */
[ 代碼段2 ]
}
7.6 盡量避免在注釋中使用縮寫,特別是不常用縮寫。
說明:在使用縮寫時,應對縮寫進行必要的說明。
8. 命名
8.1 標識符要采用英文單詞或其組合,便于記憶和閱讀,切忌使用漢語拼音來命名。
說明:標識符應當直觀且可以拼讀,可望文知義,避免使人產生誤解。程序中的英文單詞一般不要太復雜,用詞應當準確。
8.2 嚴格禁止使用連續的下劃線,下劃線也不能出現在標識符頭或結尾。
說明:這樣做的目的是為了使程序易讀。因為 variable_name 和 variable__name 很難區分,下劃線符號‘’若出現在標識符頭或結尾,容易與不帶下劃線‘’的標識符混淆。
8.3 預編譯開關的定義使用下劃線 ‘_’ 開始。
8.4 盡量避免名字中出現數字編號,如Value1、Value2等,除非邏輯上的確需要編號。
8.5 資源命名
App 里的擁有 4 種屬性,分別為一般、點擊、不能點擊、選中。 _selected _highlight _disabled,一般提供普通狀態 不帶后綴
icon icon_xxx.png
導航欄 nav_xxx.png。
標簽欄 tab_xxx.png tab_xxx_selected.png。
按鈕
btn_xxx.png
btn_xxx_selected.png
btn_xxx_highlight.png
btn_xxx_disabled.png
背景圖
bg_xxx.png bg_xxx_selected.png
8.6 類
8.6.1 類名用大寫字母開頭的單詞組合而成
所有的類名,接口名(Protocol)均以大寫字母開頭,多單詞組合時,后面的單詞首字母大寫。類,接口名必須是有意義的。
8.6.2 類名必須符合規范。
1) 繼承自UIView的類以View結尾。其他類似。
例如:
OperatorUsersInfomationView,LabelView等。
2) 繼承自ViewController的類以viewController結尾。
例如:
HomePageViewController,LoginViewController等。其他類推。
3) 所有保存數據的實體以Model結尾。
例如:
UserModel
8.6.3 在編寫派生類的賦值時,注意不要忘記對基類的成員變量重新賦值。
說明:除非在派生類中調用基類的賦值函數,否則基類變量不會自動被賦值。
正例:
- (void)viewDidLoad
{
[super viewDidLoad];
}
8.6.4 避免調用new方法
不要調用NSObject 的new方法,也不要在子類中重寫它,而是應該使用 alloc 和 init 方法來初始化retained的對象。
Objective-C代碼顯式調用 alloc 和 init 方法來創建和retain一個對象。new 的方法可能會帶來內存上調試的麻煩。
8.7 方法
8.7.1 同時取值方法前不要加前綴“get”
8.7.1 方法名用小寫字母開頭的單詞組合而成。采用駝峰法命名,第一個單詞小寫,其他大寫。
說明:方法名力求清晰、明了,通過方法名就能夠判斷方法的主要功能。方法名中不同意義字段之間不要用下劃線連接,而要把每個字段的首字母大寫以示區分。方法命名采用大小寫字母結合的形式,但專有名詞不受限制。
8.8 變量
- 變量必須起有意義的名字,使其他組員可以很容易讀懂變量所代表的意義,變量命名可以采用同義的英文命名,可使用幾個英文單詞,第一個單詞首字母小寫,其他單詞首字母大寫。
- 對于一些特殊類型的變量,命名時要帶上類型,如NSArray 的變量命名為xxxArray,其他的如xxxDictionary,xxxSize等。這樣就可以從名稱上知道是什么類型的變量。
- 對于系統的常用類作實例變量聲明時加入后綴:
UIViewController: ViewController
UIImage:image
UIImageView:ImageView
UIView:View
UILabel:Label
UIButton:Button
UINavigationBar: NavigationBar
UIToolBar:ToolBar
UISearchBar:SBar
UITextField:TextField
UITextView:TextView
NSArray:Array
NSMutableArray:MArray
NSDictionary:Dict
NSMutableDictionary:MDict
NSString:Str
NSMutableString:MStr
NSSet:Set
NSMutableSet:MSet
- 實例變量聲明時變量名前面加下劃線“_”,局部變量不用加。
8.9 常量
變量、常量和數據類型是程序編寫的基礎,它們的正確使用直接關系到程序設計的成敗,變量包括全局變量、局部變量和靜態變量,常量包括數據常量和指針常量,類型包括系統的數據類型和自定義數據類型。本章主要說明變量、常量與類型使用時必須遵循的規則和一些需注意的建議,關于它們的命名,參見命名規則。
8.9.1 對于全局變量通過統一的函數訪問
說明:可以避免訪問全局變量時引起的錯誤。
8.9.2 常量(預定義,枚舉,局部常量等)使用小寫k開頭的駝峰法。
比如kInvalidHandle,kWritePerm。
8.10 宏
8.10.1 宏定義中如果包含表達式或變量,表達式和變量必須用小括號括起來。
說明:在宏定義中,對表達式和變量使用括號,可以避免可能發生的計算錯誤。
正例:
#define HANDLE(A, B) (( A ) / ( B ))
反例:
#define HANDLE(A, B) (A / B)
8.10.2 宏名大寫字母。
正例:
#define BUTTON_WIDTH (int)320
反例:
#define kButtonWidth (int)320
8.10.3 宏常量要指定類型。
說明:不同的編譯器,默認類型不一樣。
正例:
#define BUTTON_WIDTH (int)320
反例:
#define BUTTON_WIDTH 320
8.11 建議書寫屬性名不要和系統一樣,避免發生莫名其妙的問題;特別注意的是label;屬性名不要寫成textLabel。
8.12 項目中添加plist類型文件,不要命名為info.plist,以防止和系統自帶的文件重名,發生莫名其妙的問題。
9. 表達式與語句
9.1 一條語句只完成一個功能。變量定義時,一行只定義一個變量。
說明:復雜的語句閱讀起來,難于理解,并容易隱含錯誤。
正例:
int iHelp;
int iBase;
int iResult;
iHelp = iBase;
iResult = iHelp + GetValue(&iBase);
反例:
int iBase, iResult; // 一行定義多個變量
iResult = iBase + GetValue(&iBase); // 一條語句實現多個功能,iBase有兩種用途。
9.2 在表達式中使用括號,使表達式的運算順序更清晰。
說明:由于將運算符的優先級與結合律熟記是比較困難的,為了防止產生歧義并提高可讀性,即使不加括號時運算順序不會改變,也應當用括號確定表達式的操作順序。
**正例: **
if (((iYear % 4 == 0) && (iYear % 100 != 0)) || (iYear % 400 == 0))
反例:
if (iYear % 4 == 0 && iYear % 100 != 0 || iYear % 400 == 0)
9.3 不可將布爾變量和邏輯表達式直接與YES、NO或者1、0進行比較。
說明:TURE和FALSE的定義值是和語言環境相關的,且可能會被重定義的。
正例:
設bFlag 是布爾類型的變量
if (bFlag) // 表示flag為真
if (!bFlag) // 表示flag為假
反例:
設bFlag 是布爾類型的變量
if (bFlag == TRUE)
if (bFlag == 1)
if (bFlag == FALSE)
if (bFlag == 0)
9.4 在條件判斷語句中,當整型變量與0 比較時,不可模仿布爾變量的風格,應當將整型變量用“==”或“!=”直接與0比較。
正例:
if (iValue == 0)
if (iValue != 0)
反例:
if (iValue) // 會讓人誤解 iValue是布爾變量
if (!iValue)
9.5 不可將浮點變量用“==”或“!=”與任何數字比較。
說明:無論是float還是double類型的變量,都有精度限制。所以一定要避免將浮點變量用“==”或“!=”與數字比較,應該轉化成“>=”或“<=”形式。
正例:
if ((fResult >= -EPSINON) && (fResult <= EPSILON))
反例:
if (fResult == 0.0) // 隱含錯誤的比較
其中EPSINON是允許的誤差(即精度)。
9.6 應當將指針變量用“==”或“!=”與nil比較。
說明:指針變量的零值是“空”(記為NULL),即使NULL的值與0相同,但是兩者意義不同。
正例:
if (pHead == nil) // pHead與NULL顯式比較,強調pHead是指針變量
if (pHead != nil)
反例:
if (pHead == 0) // 容易讓人誤解pHead是整型變量
if (pHead != 0)
或者
if (pHead) // 容易讓人誤解pHead是布爾變量
if (!pHead)
9.7 在switch語句中,每一個case分支必須使用break結尾,最后一個分支必須是default分支。
說明:避免漏掉break語句造成程序錯誤。同時保持程序簡潔。
對于多個分支相同處理的情況可以共用一個break,但是要用注釋加以說明。
正例:
switch (iMessage)
{
case SPAN_ON:
{
[處理語句]
break;
}
case SPAN_OFF:
{
[處理語句]
break;
}
default:
{
[處理語句]
break;
}
}
9.8 不可在for 循環體內修改循環變量,防止for 循環失去控制。
9.9 for語句的循環控制變量的取值采用“半開半閉區間”寫法。
說明:這樣做更能適應c語言數組的特點,c語言的下標屬于一個“半開半閉區間”。
正例:
int aiScore[NUM];
…
for (i = 0; i < NUM; i++)
{
printf(“%d\n”,aiScore[i])
}
反例:
int aiScore[NUM];
…
for (i = 0; i <= NUM-1; i++)
{
printf(“%d\n”,aiScore[i]);
}
相比之下,正例的寫法更加直觀,盡管兩者的功能是相同的。
10. 函數、方法、接口
10.1 避免函數有太多的參數,參數個數盡量控制在5個以內。
說明:如果參數太多,在使用時容易將參數類型或順序搞錯,而且調用的時候也不方便。如果參數的確比較多,而且輸入的參數相互之間的關系比較緊密,不妨把這些參數定義成一個結構,然后把結構的指針當成參數輸入。
10.2 對于有返回值的函數(方法),每一個分支都必須有返回值。
說明:為了保證對被調用函數返回值的判斷,有返回值的函數中的每一個退出點都需要有返回值。
10.3 對輸入參數的正確性和有效性進行檢查。
說明:很多程序錯誤是由非法參數引起的,我們應該充分理解并正確處理來防止此類錯誤。
10.4 函數(方法)的功能要單一,不要設計多用途的函數(方法)。
說明:多用途的函數往往通過在輸入參數中有一個控制參數,根據不同的控制參數產生不同的功能。這種方式增加了函數之間的控制耦合性,而且在函數調用的時候,調用相同的一個函數卻產生不同的效果,降低了代碼的可讀性,也不利于代碼調試和維護。
10.5 函數(方法)體的規模不能太大,盡量控制在200行代碼之內。
說明:冗長的函數不利于調試,可讀性差。
11. 頭文件
11.1 如果不是確實需要,應該盡量避免頭文件包含其它的頭文件。
說明:頭文件中應避免包含其它不相關的頭文件,一次頭文件包含就相當于一次代碼拷貝。
11.2 申明成員類,應該引用該類申明,而不是包含該類的頭文件。
正例:
@class SubClassName;
@interface ClassName : NSObject
{
SubClassName *_pSubClassName;
}
反例:
#import “SubClassName.h”;
@interface ClassName : NSObject
{
SubClassName *m_pSubClassName;
}
12. 可靠性
為保證代碼的可靠性。
12.1 內存使用
12.1.1 防止內存操作越界。
說明:內存操作主要是指對數組、指針、內存地址等的操作,內存操作越界是軟件系統主要錯誤之一,后果往往非常嚴重,所以當我們進行這些操作時一定要仔細。
12.1.2 必須對動態申請的內存做有效性檢查,并進行初始化;動態內存的釋放必須和分配成對以防止內存泄漏,釋放后內存指針置為nil。
12.1.3 變量在使用前應初始化,防止未經初始化的變量被引用。
說明:不同的編譯系統,定義的變量在初始化前其值是不確定的。有些系統會初始化為0,而有些不是。
12.2 指針使用
12.2.1 指針類型變量必須初始化為nil。
12.2.2 指針不要進行復雜的邏輯或算術操作。
說明:指針加一的偏移,通常由指針的類型確定,如果通過復雜的邏輯或算術操作,則指針的位置就很難確定。
12.2.3 減少指針和數據類型的強制類型轉化。
12.2.4 對變量進行賦值時,必須對其值進行合法性檢查,防止越界等現象發生。
說明:尤其對全局變量賦值時,應進行合法性檢查,以提高代碼的可靠性、穩定性。
12.2.5 非初始化方法中的alloc操作之前必須要nil判斷。
12.2.6 全局指針釋放后置為nil值。
13. 其它補充
13.1 避免過多直接使用立即數。
正例:
ViewBounds.size.height = VIEW_BOUNDS_HEIGHT;
反例:
ViewBounds.size.height = 150; Height = 150;
13.2 枚舉第一個成員要賦初始值。
正例:
typedef enum
{
WIN_SIZE_NORMAL = 0,
WIN_SIZE_SMALL
}WinSize;
反例:
typedef enum
{
WIN_SIZE_NORMAL,
WIN_SIZE_SMALL
}WinSize;
13.3 addObject之前要非空判斷。
13.4 release版本代碼去掉NSLog打印,除了保留異常分支的NSLog。
13.5 對于框架設計,邏輯層應盡量與UI層分離,降低耦合度。
13.6 同等難度下,優先考慮代碼實現窗體創建。
說明:代碼實現窗體創建容易移植,優先考慮代碼實現來替代xib實現
13.7 初始化成員變量時可以考慮用代碼塊來創建,精簡代碼。
說明:這個方法有一個優點,所有的變量都在代碼塊中,也就是只在代碼塊的區域中有效,這意味著可以減少對其他作用域的命名污染,但缺點是可讀性比較差。
正例:
self.opView1 = ({
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((self.view.frame.size.width-100)/2, (self.view.frame.size.height-100)/2-100, 100, 100)];
imageView.image = [UIImage imageNamed:@"93F5E460-9D31-4780-8511-37FF91033402"];
[self.view addSubview:imageView];
imageView;
});
13.8 避免循環引用
說明:如果【block內部】使用【外部聲明的強引用】訪問【對象A】, 那么【block內部】會自動產生一個【強引用】指向【對象A】
如果【block內部】使用【外部聲明的弱引用】訪問【對象A】, 那么【block內部】會自動產生一個【弱引用】指向【對象A】
正例:
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{
__strong typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil(搶占的時候,strongSelf 還是非 nil 的)
[strongSelf doSomethingElse]; // strongSelf != nil }
else { // Probably nothing... return;
}
};
反例:
__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
[weakSelf doSomething]; // weakSelf != nil
// preemption, weakSelf turned nil
[weakSelf doSomethingElse]; // weakSelf == nil
};
13.9 建議加載xib,xib名稱用NSStringFromClass(),避免書寫錯誤
13.10 場景需求:在繼承中,凡是要求子類重寫父類的方法必須先調用父類的這個方法進行初始化操作;建議:父類的方法名后面加上NS_REQUIRES_SUPER; 子類重寫這個方法就會自動警告提示要調用這個super方法。
正例:
// 注意:父類中的方法加`NS_REQUIRES_SUPER`,子類重寫才有警告提示
- (void)prepare NS_REQUIRES_SUPER;
13.11 id類型屬性不能用點語法,調用get方法只能用中括號調用,[id 方法名],利用iOS9新特性泛型就可以; 比如數組。
13.12 如果不是屬性,盡量不要點語法,一個老程序員的建議。
13.13 使用第三方框架,盡量不要更改內部文件,而應該再次封裝,個性定制。
13.14 接手一個新項目,快速的調試,查看某個模塊或者方法的作用,需要注釋掉一個方法,或者某個代碼塊,直接寫return;而不是全選,注釋掉
13.15 監聽鍵盤的通知建議:
UIKIT_EXTERN NSString *const UIKeyboardWillChangeFrameNotification
而不是,下面代碼;因為鍵盤可能因為改變輸入法,切換成表情輸入,切換成英文,那么frame可能會變高,變矮,不一定會發出下面這些通知,但是肯定會發上面的通知
UIKIT_EXTERN?NSString *const UIKeyboardWillShowNotification;
UIKIT_EXTERN?NSString *const UIKeyboardDidShowNotification;
UIKIT_EXTERN?NSString *const UIKeyboardWillHideNotification;
UIKIT_EXTERN?NSString *const UIKeyboardDidHideNotification;
13.16 大量操作圖層會可能造成應用很卡,給用戶體驗差,所以盡量不要操作圖層;比如設置按鈕圓角,比如給button設置圓角。
13.17 給分類擴充方法,建議加上前綴,比如第三方框架SDWebImage,這樣做跟系統的方法很容易區分開,減少了程序員之間的溝通成本,同理跟分類添加屬性(利用運行時),建議加前綴,以防止蘋果官方過一段時間添加了一模一樣的屬性名,比如給UITextField分類添加了placeholderColor這個屬性,萬一某天官方給placeholder擴充了這個命名一模一樣的屬性,那么就不好了。
13.18 抽取方法,或者寫工具類,能寫類方法,盡量寫成類方法,減少了創建對象的步驟,比如給UIView擴充分類加載xib,viewWithXib。
13.19 聲明一個屬性,如果是對象,比如數組,不能以new單詞開始,否則直接報錯,因為new在OC中是生成一個對象的方法,有特殊含義;比如:
@property (nonatomic,strong) NSMutableArray *newTopicsM;
注意:如果newtopicsM是一個單詞(區別于駝峰標志),這樣寫不會報錯;如果是基本數據類型則不會報錯,比如
@property (nonatomic,assign) int newNumber;
13.20 在自定義方法中,and這個詞的用法應該保留。它不應該用于多個參數來說明,就像initWithWidth:height以下這個例子:
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
而不應該
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;