iOS代碼規範和優化建議

公司須要寫一個代碼規範,本人第一次寫,完成後記錄在blogweb

1、命名規範數據庫

一、類定義編程

例: ThisIsAClass,

採用蘋果推薦的方式,首字母大寫,多個有實際意義的英文單詞組成,每一個單詞的首字母大寫。json

在此基礎上類名須要體現出這個類的類型swift

試圖控制器:ThisIsAViewController
試圖:ThisIsAView
按鈕:ThisIsAButton
等等數組

我的不建議使用縮寫,由於代碼補全很是完善,單詞再長也不會影響方便性,但縮寫影響可讀性
可討論決定是否可以使用相似 ThisIsAVC,ThisABtn,之類的縮寫方式緩存

二、屬性安全

例:thisIsAObjectruby

首字母小寫,多個有實際意義的英文單詞組成,每一個單詞的首字母大寫。bash

在此基礎上,須要提示這個變量的類型或者用途。

例如:

UITextField * userNameTextField;

UIButton * leftTopButton,doSomeActionButton;

等等

縮寫問題同上,可討論。

三、成員變量

_menberObject

在屬性的基礎上,須要如下劃線開頭。

其餘規則和屬性同樣。

四、局部變量

參照屬性

五、函數命名

首字母小寫,多個有意義的單詞組成,:後面首字母小寫

例:

- (void)loginButtonDidPressed; - (void)loginWithUserName:(NSString) userName password:(NSString *) password;

等等

六、函數形參

參照屬性

如有可能與屬性命名相同或者衝突,建議加上in,a等前綴

例:

- (void) someMethod:(id) inValue anotherObject:(id) aObject
{
    _vlaue = inVlaue;
    self.object = aObject;
}

7 、前綴

因爲oc沒有命名空間的概念,全部蘋果建議使用前綴來防止第三方庫、子工程之間命名衝突。

目前咱們的工程中有的沒有前綴,有前綴的又不統一,須要討論是否統一。

2、代碼組織

一、概述

目前咱們的工程都採用MVC構架

因此頁面代碼都至少要包含,View(特別簡單的可能省略),VewController(UIVewController子類),ModelManager(這麼命名是區分於瘦model,也就是entity)

三者之間最好經過接口互相訪問,隱藏實現細節。

好比,VC不須要知道view有哪些label,textfileld,只須要view提供加載數據的方法就能夠,實現鬆耦合。這樣若是隻是頁面排版變了,只要修改接口實現就能夠,不用修改其餘部分。這也是swift發佈後,蘋果提出的面相接口(協議)編程的思想。

規則提出的目的,都是指導咱們對各模塊之間職能劃分,使各自功能單一,鬆散耦合,任一一方修改,儘量的不影響其餘模塊。

二、頁面
UIView:只作顯示邏輯不作業務邏輯,它應該是純被動的接受VC提供的數據,進行展現。
對用戶操做進行收集,提示VC事件發生。

下面提供一些開發場景的處理建議:

case 1

若是view包含一個imageview,性別變量sex,值爲male,顯示圖a,sex爲female,顯示圖b,這種邏輯應該在VC中實現,而不該該把sex傳給給View去保存。

//VC中
- (void) someMethod
{
    if (self.sex == male) //假設male爲枚舉
    {
        [view loadImage:imageA];
    }
    else
    {
        [view loadImage:imageB];
    }
}

//view中

- (void)loadImage:(UIImage *)inImage
{
    self.imageView.image = inImage;
}

case 2

不要在init方法中,去取自身的frame,並以此對子view設置frame,而應該在layoutSubviews中設置
由於不少時候,init方法中,view自己的frame並非最後顯示的frame

- (void)layoutSubviews
 {
    // 必定要調用super的方法
    [super layoutSubviews];

    //子view的frame設置
    …
}

case 3

只是堆砌子View的狀況通常不太須要自定義view。
自定義view,其實更應該發生在須要重載drawRect方法,或者事情響應touchesBegan/touchesEnded等方法時。而大部分狀況其實不須要重載這些方法。只須要addSubvew。
這種狀況其實更應該考慮是建一個子視圖控制器,畢竟管理一個視圖層級(view hierarchy)是VC的事情。

三、視圖控制器

試圖控制器負責:從而且只從modelManager獲取數據,展現到View上。而且接受modelManager數據變化發生時通知,提供回調方法。

開發場景:
case 1

loadView:通常不建議重載,實在要用根據文檔所訴,只應該建立視圖層級(view hierarchy)也就是隻應該作alloc和addSubview。而且若是重載它了,不要調用super,額外的賦值需在viewDidload以後再作。
注意:若是使用了xib或者storyboad就不要重載這個函數。而應該使用awakeFromNib。

case 2
viewDidload:做爲入口,但願它能比較簡潔直觀,因此建議它內部只出現toDoList型的函數,具體的實現到各個函數中實現。
例如:

- (void) viewDidLoad
{
    [super viewDidLoad];

    [self setupViews];
    [self registerNotifications];
    [self setupModelManager];
    [self doOtherThing];
}

建議在viewDidload中使用自動佈局來管理View及子view的frame

case 3

viewwill/DidlayoutSubviews:這對函數在 view調用layoutSubviews先後調用。若是你沒用使用自動佈局,應該在這兩個函數中對view進行位置的設置。

case 4

關於展現用的數據,除非必要,不建議用額外的屬性/成員變量保存。而直接經過modelManager的函數得到。以便由modelManager控制數據保存的位置是內存變量,仍是持久化方式。
實在須要在內部函數間傳遞,也儘可能根據最小知道原則,保存索引,狀態值之類的數據。

四、modelManager

modelmananger負責從一個或多個數據源,採集數據業務操做後提供給VC,數據來源多是數據庫,內存緩存,網絡等中的一個或多個。

開發場景

case 1

對VC隱藏數據源的原始數據結構。
也就是說盡可能不要把數據源原始數據直接交給VC,而應該與VC約定好相互之間須要傳遞的最小單元(實體/瘦model),把從一個或多個數據源獲取的原始數據,轉化成該最小單元。提供給VC

case 2

與VC交互的協議接口定義,應足夠獨立,一個事件/一次數據請求,一般不該該須要調用到其餘接口(這裏不是說不能調其餘內部函數,而是對VC 開放的其餘接口)

case 3

接口實現優先進行內存操做,內存沒有的數據再操做數據庫,尚未再進行網絡操做。實現多級緩存及時反饋。

case 4

頁面之間的數據變動響應,應該在modelManager之間通知和響應
具體來講:
vc1某操做,致使vc1的modelManager有數據變動,這個變動影響到v2,那麼不該該由v2去接受這個變動通知,而應該由vc2的modelManager接受並修改自身的數據,經過本身的數據變動接口通知vc2。
也就是說一個VC只接受一個modelManager的數據更新通知。

case 5

儘管上面的建議一個VC只對應一個modelManager。但有些場景下,多個VC能夠共享一個modelManager。
例:
設置功能下有多個子設置項,這些設置項是不一樣子頁面操做去設置的而且很簡單,當咱們把設置項的數據做爲一個總體去考慮的時候,這些子設置項操做的其實應該是同一個數據對象。此時應該支持這些頁面傳遞modelManager對象來共享它。

五、其餘衍生組件

在MVC的框架下,可根據功能單一原則,拆分出多個功能組件,供三大框架模塊使用,好比數據庫模塊,網絡模塊,加密模塊,播放器模塊等等。

3、代碼優化建議

case 1

任何狀況下老是使用setValue:forKey代替setObject:forKey
由於前者在value非nil狀況下等於後者。
在value爲nil刪除key所對應的鍵值。因爲賦值時通常當前並不會存在key的鍵值,即便是覆蓋某對鍵值,在值爲空的狀況下,刪除了它也是合理的。
因此前者效果徹底等效於檢測value非空後調用後者。
後者object爲nil則直接crash。
因此使用setValue:forKey更安全。

case 2

隱藏沒必要要的接口和屬性。
那些只在內部使用的屬性和函數,應定義在類擴展之中,把少數願意暴露出去的函數和屬性定義在頭文件中。

case 3

儘可能不要使用成員變量,用類擴展中的屬性代替它。而後老是使用self.去訪問它。
這樣有更清晰的內存管理。
而且,當有一天你忽然須要在賦值或者取值的時候作一些事情的時候,你就只須要在set/get中操做。而不用整個類裏面處處修改了。

case 4

儘可能不要阻塞主線程,儘可能把非UI操做放到其餘線程執行,完成後回到主線程通知UI刷新。

case 5

NSSet/NSArray,他們的區別是,NSSet是無序的,當一個數據集合不關注排序時,使用NSSet,查找元素是否存在和移除效率是高於NSArray的。
例:
維護一個操做中的數據集合,操做開始後加到集合中,操做完成後移除出集合,這種狀況下應當使用NSSet

case 6

不管何時經過索引訪問數組都應該進行越界判斷。或者try catch

case 7

懶加載,對不經常使用的View,不要在頁面打開時就建立,採用懶加載的方式去實現。以減小頁面啓動時間。

case 8

預加載,對於加載速度比較慢,又須要進入頁面就直接顯示的數據,考慮在適當的時間提早加載好,駐留在內存中。空間換時間。

case 9

tableView的使用除了注意cell正確重用之外,還需注意,數據源的count變化,特別是變小是要及時reloadData。若是使用insertRow/removerow操做,必定要保證數據源數量等於變化前數量加/減變化數量,不然就會crash

case 10

實體中只能進行簡單的數據操做,例如json轉字典,數值轉字符,取整之類的。不能進行業務操做,如存取數據庫,網絡操做等。

case 11

使用大量臨時變量時,可用@autoreleasepool來包含起來及時釋放。