隨著代碼量的增加和項(xiàng)目的增大以及項(xiàng)目合理或不合理的迭代背镇,代碼會(huì)顯得冗余和雜亂。良好的代碼規(guī)范和和規(guī)則以及設(shè)計(jì)良好的封裝對(duì)項(xiàng)目的迭代擴(kuò)展以及程序員本身無疑是一件好事丰刊,但是事實(shí)是被大多數(shù)程序員忽略隘谣,下面是自己對(duì)自己的一點(diǎn)代碼規(guī)范要求
- <span id="使用屬性_ref_id">使用屬性</span>
從C++的官方推薦和Java Bean規(guī)范來講,都是把變量私有化啄巧,對(duì)外提供getter和setter方法寻歧,這就是我們常說的“屬性”,對(duì)于Objective-C的優(yōu)雅的屬性語法秩仆,我們更應(yīng)該在可以使用變量和屬性的選擇中優(yōu)先選擇屬性码泛,即:
不要使用
@interface MyObject : NSObject {
NSString *name;
...
}
@end
應(yīng)該使用
@interface MyObject : NSObject
@property (nonatomic, copy) NSString *name;
@end
使用這種命令方式的優(yōu)點(diǎn)不僅僅在于統(tǒng)一了代碼規(guī)范,而且更重要的是由于提供了setter/getter方法逗概,可以更好的保護(hù)數(shù)據(jù)弟晚,以及在set/get的時(shí)候作額外的操作,并且使用@property的形式方便運(yùn)行時(shí)捕捉到屬性信息以便作底層次的處理逾苫。
但是卿城,可能有人問使用變量會(huì)不會(huì)比使用屬性效率要高?回答:很可能是铅搓,所以你寫項(xiàng)目全用匯編或機(jī)器語言寫好了瑟押,不要考慮其他語言。為了這一點(diǎn)點(diǎn)所謂的效率而失掉優(yōu)雅的代碼特性是錯(cuò)誤的做法星掰。而且可以負(fù)責(zé)任的說多望,真正的coding高手眼中的效率問題絕不是這種淺薄的棄掉屬性用變量換取執(zhí)行效率的問題。
-
<span id="ARC中屬性的省略寫法_ref_id">ARC中屬性的省略寫法</span>
ARC自從推出至今已很多年氢烘,我們的項(xiàng)目一律采用ARC內(nèi)存管理方式處理怀偷。
在ARC中屬性默認(rèn)為strong,這個(gè)字段可以省略不寫播玖,即:
不要使用:
@property (nonatomic, strong) Person *person;
建議使用:
@property (nonatomic) Person *person;
對(duì)于基本類型同樣可以省略assign椎工,因?yàn)閷?duì)于基本數(shù)據(jù)類型,不存在什么strong或retain:
不要使用:
@property (nonatomic, assign) NSInteger age;
建議使用:
@property (nonatomic) NSInteger age;
在一些第三方庫的代碼里,可能見到省略的或不省略的寫法维蒙,在這里掰吕,咱們只是為了統(tǒng)一規(guī)范而已,無用與不用的理由颅痊。
-
<span id="屬性的統(tǒng)一寫法_ref_id">屬性的統(tǒng)一寫法</span>
@property (nonatomic) Person *person;
@property的括號(hào)間有間隔殖熟,nonatomic和strong間有間隔,指針*號(hào)靠近對(duì)象而不是靠近類斑响。
只是為了清晰代碼和規(guī)范菱属,無用與不用的理由。
-
<span id="屬性的一些使用技巧_ref_id">屬性的一些使用技巧</span>
在自己封裝一些類庫時(shí)恋捆,屬性的寫法有些注意事項(xiàng):
-
不必要提供出去的屬性寫在.m中照皆,形如:
// MyClass.m @interface MyClass() @property (nonatomic) SomeObject *obj; // 既然沒必要提供出去,就把它私有化沸停,外界不讓訪問 @end @implementation MyClass ... @end
-
提供出去的BOOL類型的屬性膜毁,其getter方法寫為:isXxx, 形如:
@property (nonatomic, getter=isValid) BOOL valid; // 這樣調(diào)用set方法為:obj.valid = YES; // 調(diào)用get方法為:BOOL isValid = obj.isValid;
這種方式更貼近人性化,我們看Apple的官方類愤钾,對(duì)于這類BOOL類型的屬性瘟滨,也是如此,比如:
// UIKit/UIControl.h @property(nonatomic,getter=isEnabled) BOOL enabled; @property(nonatomic,getter=isSelected) BOOL selected;
-
一些屬性在外部只讀能颁,但在內(nèi)部需要set的做法:
// MyClass.h @interface MyClass : NSObject @property (nonatomic, readonly) SomeObject *obj; // 對(duì)外只讀 @end // MyClass.m @interface MyClass() @property (nonatomic, readwrite) SomeObject *obj; @end @implementation MyClass - (void)myMethod { self.obj = [[SomeObject alloc] init]; // 在內(nèi)部可以賦值 } @end
?
-
-
<span id="駝峰命名法_ref_id">駝峰命名法</span>
駝峰命名法Camel-Case從早期的lower-case命名法發(fā)展而來杂瘸,形如“好學(xué)生”的lower-case命名法為:good_student, 其駝峰命名法為:goodStudent, 這些都被大家所習(xí)慣和熟識(shí),在OC中伙菊,類名第一個(gè)字母必須大寫败玉,變量第一個(gè)字母小寫等規(guī)則不必多說,但值得一提的是
不要使用:
Student *gS;
建議使用:
Student *goodStudent;
在像Objective-C類似的這種高級(jí)語言中镜硕,絕不建議過分的省略單詞的寫法运翼,如果寫成gS, 只有你和鬼知道是什么意思,多打一點(diǎn)字清晰一些不是什么壞事兴枯。有位老師在教授PHP的課堂中曾說到一個(gè)學(xué)生起的變量名為:fdckfs, 后來一問才知道這個(gè)意思是”房地產(chǎn)開發(fā)商“血淌。
-
<span id="類中屬性/變量的多余寫法_ref_id">類中屬性/變量的多余寫法</span>
比如學(xué)生類:Student, 有兩個(gè)屬性:姓名和年齡
不要使用:
@property (nonatomic, copy) NSString *studentName; @property (nonatomic) NSInteger studentAge;
建議使用:
@property (nonatomic, copy) NSString *name; @property (nonatomic) NSInteger age;
適當(dāng)想一下就可以知道,在學(xué)生類里财剖,name就是學(xué)生的名字悠夯,沒必要再強(qiáng)調(diào)用studentName表示學(xué)生姓名,在Student里用studentName表示學(xué)生姓名是一種完全多余的寫法躺坟。
-
<span id="方法的命名規(guī)則_ref_id">方法的命名規(guī)則</span>
不必多說沦补,仿官方形式:
- (void)viewDidLoad { // ... }
-或+距擴(kuò)號(hào)有一個(gè)空格,方法名駝峰寫法咪橙,首字母一律小寫策彤。
-
<span id="方法的書寫和注釋_ref_id">方法的書寫和注釋</span>
-方法和+方法栓袖,-/+符合和括號(hào)留空隔,方法體左括號(hào)靠近代碼書寫店诗,for循環(huán);內(nèi)留空隔,形如:
- (void)viewDidLoad { // ... for (NSInteger i = 0; i < 100; i++) { // ... } }
其實(shí)和官方API寫法一致而已音榜,養(yǎng)成習(xí)慣庞瘸,統(tǒng)一起來不致看著亂。
注釋采取Apple推薦注釋赠叼,形如:
/** * @param keyValues 字典(可以是NSDictionary擦囊、NSData、NSString) * @cls 即data所對(duì)應(yīng)的類瞬场,如果傳為nil, 則不對(duì)data進(jìn)行解析 * @return 新建的CommonModel對(duì)象 */ + (instancetype)commonModelWithKeyValues:(id)keyValues dataClass:(Class)cls;
對(duì)于//注釋,//符合和注釋文字間留出一個(gè)空隔, 這也是官方的寫法贯被,形如:
[obj testMethod]; // 這是注釋文字妆艘,和//符合留一空格
-
<span id="擴(kuò)展類的方法命名注意事項(xiàng)_ref_id">擴(kuò)展類的方法命名注意事項(xiàng)</span>
我們看一些第三方庫,比如SDWebImage, MJExtension這些比較特殊批旺,它們很多是擴(kuò)展類Category,比如SDWebImage中有對(duì)UIImageView的擴(kuò)展汽煮,MJExtension有對(duì)NSObject的擴(kuò)展搏熄。拿SDWebImage中的UIImageView+WebCache.h中的方法聲明中示例:
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;
它的命名方式為什么加了前綴sd_, 這不符合一般的方法命名規(guī)范,其實(shí)這主要是為了防止有一天系統(tǒng)給UIImageView也加了setImageWithURL:placeholderImage:或是為了防止其他第三方庫也實(shí)現(xiàn)了setImageWithURL:placeholderImage:這個(gè)方法暇赤。
這種事情的發(fā)生并不是什么巧合心例,比如我在AFNetworking的UIImageView+AFNetworking中也發(fā)現(xiàn)了類似的代碼:
- (void)setImageWithURL:(NSURL *)url placeholderImage:(nullable UIImage *)placeholderImage;
如果SDWebImage沒有加前綴,那么這兩個(gè)方法的方法簽名就都是:
setImageWithURL:placeholderImage:
試想翎卓,在這兩個(gè)庫中契邀,這個(gè)方法都是對(duì)UIImageView的Category,如果SDWebImage真的沒有對(duì)方法加sd_前綴(早版本的SDWebImage確實(shí)沒有加前綴)失暴,在項(xiàng)目中引入SDWebImage這個(gè)庫的同時(shí)又引入了AFNetworking, 會(huì)怎么樣坯门?這就會(huì)出現(xiàn)蘋果官網(wǎng)上說的“Undefined problem”未知問題。
啟示:如果我們要封裝自己的Category, 方法名也要加自己的統(tǒng)一前綴逗扒。因?yàn)镃ategory不同于繼承古戴,對(duì)于繼承出來的兩個(gè)類,其對(duì)象調(diào)用相同的實(shí)例方法不會(huì)有什么問題矩肩,但Category不一樣现恼,比如上面對(duì)imageView如果調(diào)用相同的方法,編譯器就無法識(shí)別了。
-
<span id="類的命名規(guī)范_ref_id">類的命名規(guī)范</span>
在傳統(tǒng)的C++語言叉袍、高級(jí)的Java始锚、C#語言中為了解決類名的重復(fù)問題采用了"命名空間"或"package"的方法來解決,大致意思是說在命名空間UI下的Button類和命名空間LB(聯(lián)璧)下的Button類以及其他命名空間下的Button類不是同一個(gè)類喳逛,不同命名空間下可以有相同的類名瞧捌,這好比上海市的“小明”和蘇州市的“小明”不是一個(gè)小明一樣,但可惜的是润文,OC并沒有這樣的機(jī)制姐呐,因此取而代之的是在類名前加前綴,隨著一門語言體系的增大典蝌,我個(gè)人認(rèn)為這種在類名加前綴的做法并不是非常的好曙砂,但好在Apple給我們封裝的類都很經(jīng)典和強(qiáng)悍,在類名前加前綴也使用這些類的用處明顯骏掀。所以毫無疑問鸠澈,我們的項(xiàng)目也要保持這種習(xí)慣。
按照傳統(tǒng)的MVC模式來分砖织,統(tǒng)一起見類名前加前綴如下:
Model: LB... View: LV... Controller: LC...
-
<span id="使用屬性_ref_id">枚舉寫法</span>
OC自定義了枚舉的寫法侧纯,可以很好的聲明枚舉的類型眶熬,比如枚舉類型為NSInteger, 所以
不建議使用:
typedef enum { LVStyleActionSheet = 0, LVStyleAlert } LVStyle;
建議使用:
typedef NS_ENUM(NSInteger, LVStyle) { LVStyleActionSheet = 0, LVStyleAlert };
-
<span id="兩種block的常規(guī)寫法_ref_id">兩種block的常規(guī)寫法</span>
"返回值為void,沒有跟參數(shù)的block類型"拳缠,Apple已經(jīng)為我們定義好了:
typedef void (^dispatch_block_t)(void);
為了統(tǒng)一起見窟坐,關(guān)于這種block類型哲鸳,項(xiàng)目中也應(yīng)該用dispatch_block_t徙菠,因?yàn)槌S玫脑蛐霰迹珹pple寫好了這個(gè)類型,另外還有一種block類型常用:"返回值為void挤茄,跟一個(gè)參數(shù)的block類型"驮樊,所以我們可以把這兩種常用的block類型寫進(jìn)pch文件,形如:
typedef dispatch_block_t BlockVoidType; // 只是為了向駝峰命名靠近雕沿,重新typedef系統(tǒng)類型 typedef typedef void (^BlockIdType)(id); // 返回值為void, 跟一個(gè)id參數(shù)的block類型
-
<span id="少用tag值_ref_id">少用tag值</span>
項(xiàng)目中如果有多個(gè)類似的控件审轮,很多情況可以用tag值區(qū)分疾渣,比如一個(gè)頁面有10個(gè)按鈕榴捡,每點(diǎn)擊一個(gè)按鈕把其中一個(gè)學(xué)生的信息傳給其他控制器吊圾,它們觸發(fā)的方法都是buttonClick: 那么為了區(qū)分是哪個(gè)按鈕, 可以用tag值區(qū)分项乒,代碼形如:
- (void)buttonClick:(UIButton *)button { if (button.tag == 100) { Student *student = self.studentArray[button.tag-100]; // ... } else if (button.tag == 101) { // .. } ... }
這種是極其糟糕的做法檀何,這種寫法除了自己很少有人知道tag為100代表的是什么频鉴,一般來說我們的做法有兩種砚殿,一是封裝自己的Button:
@interface MyButton @property (nonatomic) Student *student; // ... @end
但是如果項(xiàng)目已寫成似炎,大量的修改這種代碼并不見得好羡藐,所以我們采用第二種方式:
二是對(duì)Button進(jìn)行擴(kuò)展仆嗦,通過動(dòng)態(tài)關(guān)聯(lián)讓UIButton具有Student屬性瘩扼,針對(duì)這種需求集绰,為了統(tǒng)一起見,在項(xiàng)目中我們對(duì)NSObject進(jìn)行擴(kuò)展給其附加一個(gè)對(duì)象:
// NSObject+LBAttatchObject.h #import <Foundation/Foundation.h> @interface NSObject (LBAttatchObject) @property (nonatomic) id lbAttachObject; @end
#import "NSObject+LBAttatchObject.h" #import <objc/runtime.h> @implementation NSObject (LBAttatchObject) - (id)lbAttachObject { return objc_getAssociatedObject(self, _cmd); } - (void)setLbAttachObject:(id)lbAttachObject { objc_setAssociatedObject(self, @selector(lbAttachObject), lbAttachObject, OBJC_ASSOCIATION_RETAIN); } @end
-
<span id="統(tǒng)一模型層_ref_id">統(tǒng)一模型層</span>
用AF從服務(wù)器上down下來數(shù)據(jù)之后,用MJExtension或其他第三方庫的反射機(jī)制把數(shù)據(jù)轉(zhuǎn)換成模型浴讯,盡量不要使用從字典中取key-value值蔼啦,把數(shù)據(jù)對(duì)象化询吴,為了便于處理,在MJ的基礎(chǔ)上做CommonModel, 統(tǒng)一處理服務(wù)器返回的數(shù)據(jù)
// LBCommonModel作為整個(gè)App的Model唠摹,不同點(diǎn)在于data字段 @interface LBCommonModel : NSObject @property (nonatomic, copy) NSString *msg; @property (nonatomic) id data; @property (nonatomic) NSInteger code; @property (nonatomic, copy) NSString *totalAmt; /** * @param keyValues 字典(可以是NSDictionary、NSData盗温、NSString) * @cls 即data所對(duì)應(yīng)的類卖局,如果傳為nil, 則不對(duì)data進(jìn)行解析 * @return 新建的CommonModel對(duì)象 */ + (instancetype)commonModelWithKeyValues:(id)keyValues dataClass:(Class)cls; @end
-
<span id="Controller命名可去掉View_ref_id">Controller命名可去掉View</span>
前面說過批销,控制器前綴字符:LC, 如果所有的控制器再加上ViewController, 顯得有些長,比如:LCFinalcialViewController, 為了統(tǒng)一起見丘逸,即然是控制器,突出Controller, 不用帶View, 即命名控制器格式為:
LCXxxController LCFinalcialViewController -> LCFinalcialController
-
<span id="慎用通知_ref_id">慎用通知</span>
通知NSNotificationCenter是KVO設(shè)計(jì)模式的重要實(shí)現(xiàn)之一劲妙,但對(duì)于一般的界面之間的回調(diào)等涛舍,少用通知,通知會(huì)導(dǎo)致系統(tǒng)的維護(hù)性很差肛搬,一旦通知用的多了温赔,很難知道哪里注冊(cè)了通知陶贼,哪里受通知的影響拜秧,這會(huì)變的雜亂枉氮。
通知應(yīng)當(dāng)用在系統(tǒng)層次聊替、全局層次的影響上惹悄,比如統(tǒng)一處理鍵盤遮擋輸入框的問題泣港,而不是用在簡單的傳值上面爷速。為了更有效的管理通知惫东,統(tǒng)一封裝類LBNotificationManager來管理系統(tǒng)上所有的通知颓遏。形如:
ace LBNotificationManaager : NSObject // 通知 + (void)registNotifyOfDownloadSoftSuccess:(id)observer selector:(SEL)selector; // 注冊(cè)通知 + (void)postNotifyOfDownloadSoftSuccess:(id)object userInfo:(NSDictionary *)userInfo; // 發(fā)送通知 + (void)removeNotifyOfDownloadSoftSuccess:(id)observer object:(id)obj; // 取消通知 @end
-
<span id="表單數(shù)據(jù)的抓取_ref_id">表單數(shù)據(jù)的抓取</span>
在傳統(tǒng)的web開發(fā)中滞时,表單form提供給用戶填寫信息,當(dāng)點(diǎn)擊提交submit的時(shí)候坪稽,再把用戶填寫的表單數(shù)據(jù)提交給服務(wù)器曼玩,在iOS開發(fā)中,我們姑且認(rèn)為諸如UITextField等控件為表單控件窒百,當(dāng)點(diǎn)擊"注冊(cè)黍判、登錄"等提交按鈕時(shí),我們通常做法是從這些控件中提取出值然后做處理篙梢,比如賦值給模型顷帖,過濾一些信息,針對(duì)這種需求渤滞,我們可以做些處理贬墩,點(diǎn)擊提交時(shí)陶舞,讓輸入控件的值自動(dòng)賦給模型颁井,而不必每次再從控件中取值葵硕。
這里有一個(gè)笨拙的方案: ZZBindDemo_2,但這種處理方式還是比較笨的,如果結(jié)合思路用RAC處理會(huì)好很多,推薦使用RAC解決這個(gè)問題喂很,參照當(dāng)前目錄下 BindDemo