Object-C編程規(guī)范

主要參考:github:https://github.com/raywenderlich/objective-c-style-guide 等匯總

<p>
1.變量命名規(guī)范
定義有意義的變量,采用駝峰方式。變量名能直接反應(yīng)其意義和類型除去NSNumber, NSString外,對于NSArray, NSDictionary 或其它對象,采用+后綴的方式。標識其類型。
<pre>
<code>//good</code>
<code>@property (nonatomic, strong) UIImageView *logoImageView;</code>
<code>@property (nonatomic, strong) UILabel *titleLabel;</code>
<code>@property (nonatomic, strong) UILabel *subTitleLabel;</code>
<code>@property (nonatomic, strong) UIButton *payButton;</code>

<code>NSNumber *cartNum;</code>
<code>NSString *promotionTitle;</code>
<code>NSArray *trades; // 以英文單詞復(fù)數(shù)形式</code>
<code>NSDictionary *adDict; // 以dict結(jié)尾</code>
<code>// Bad</code>
<code>@property (nonatomic, strong) UIImageView *imageView1;</code>
<code>@property (nonatomic, strong) UIImageView *imageView2;</code>
</pre>

  1. 常用類型變量初始化(NSString, NSDictionary, NSArray, and NSNumber)
    <pre>
    <code>// Good</code>
    <code>NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];</code>
    <code>NSDictionary *productManagerDict = @{
    @"iPhone": @"Kate",
    @"iPad": @"Kamal",
    @"Mobile Web": @"Bill"
    };</code>
    <code>NSNumber *shouldUseLiterals = @YES;</code>
    <code>NSNumber *buildingZIPCode = @10018;</code>

<code>// 獲取NSArray成員和NSDictionary成員的讀取和對應(yīng)的Mutable類型的設(shè)置,優(yōu)先采用簡化方式:</code>
<code>NSString *firstName = names[0];</code>
<code>NSString *manager = productManagerDict[@"iPad"];
</code>

<code>// Bad</code>
<code>NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];</code>
<code>NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];</code>
<code>NSNumber *shouldUseLiterals = [NSNumber </code><code>numberWithBool:YES];</code>
<code>NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];</code>

<code>NSString *firstName = [names objectAtIndex:0];</code>
<code>NSString *manager = [names objectForKey:@"iPad"];</code>
</pre>
3.類,方法接口和屬性定義
<p>.h文件中僅僅放置需要提供給其他地方使用的屬性和方法(需要暴露的東西),其他屬性可以直接定義在.m文件中, 不要把所有方法或?qū)傩远祭?h中,盡量使用property方式定義屬性,避免原來直接定義ivar的形式;
.h文件中,盡量減少直接引用其他頭文件,可以通過@class方式聲明即可。在.m文件中進行對應(yīng)的引用。
在block中,不要用ivar形式訪問屬性,一律使用self.property的形式,防止隱性的self retain被忽視,從而造成retain cycle.
對應(yīng)的實現(xiàn)方法塊可以通過#pragma -mark 來進行分組標識
</p>
<pre>
<code>// .h文件</code>
<code>// 通過@class減少直接import頭文件</code>
<code>@class BSTDemo1,BSTDemo2;</code>
<code>@interface BSTService : NSObject</code>
<code>+ (instancetype)sharedManager;</code>
<code>- (void)loadXXWithType:(NSArray *)XXTypes block:(LoadSuccessBlock)block;</code>
<code>- (void)clearCache;</code>
<code>@property (nonatomic, strong) NSArray *XXKeys;</code>
<code>@property (nonatomic, assign) BOOL hasXX(isXX);
</code>
<code>@end</code>

<code>// .m文件</code>
<code>@interface BBAdsService ()</code>
<code>// 定義私有屬性</code>
<code>@property (nonatomic, strong) BSTDemo1 *XXModel;</code>
<code>@end</code>
<code>// 對外接口</code>
<code>- (void)loadXXWithType:(NSArray *)XXTypeArray block:(LoadSuccessBlock)block</code>
{
<code>// ...</code>
<code>[self doGetXX:XXTypeArray expires:XXX block:^(BBXXModel *model)</code> {
<code>// ...</code>
<code>// 注意,此處有對self的retain,此block不能為self的成員,否則就retain cycle了</code>
<code>self.displayXXModel = model;</code>
<code>// Bad</code>
<code>// _displayXXModel = model;</code>
<code> // ...</code>
}];</code>
}
</pre>

  1. 工程目錄組織和代碼分塊 #pragma mark

.m文件中代碼組織:
第一部分,放初始化,dealloc相關(guān)代碼,無需添加marker
第二部分,對于有生命周期的,通過life cycle標識,把所有的生命周期方法集中到一起處理。無生命周期的自定義類或view等,無需添加
第三部分,所有實現(xiàn)的delegate,需要一一明確分塊標識出來。
第四部分,以event handler為標識,放所有事件響應(yīng)代碼,包括button,gesture, notification, kvo等
第五部分,放自定義方法,可以自己定義marker, 如不想定義,則直接用methods
最后一部分,以accessors為標識,放所有g(shù)etter 和 setter代碼
<pre>
<code>// 初始化的,可不用</code>
<code>- (instancetype)init;</code>
<code>- (void)dealloc;</code>

<code>// 對于 ViewController 或 Application, 沒有生命周期則不需要</code>
<code>#pragma mark - life cycle</code>
<code>- (void)viewDidLoad;</code>

<code>// 所有的delegate實現(xiàn),必須要一一標識出來</code>
<code>#pragma mark - xxxxDelegate</code>
<code>- (void)onSelectIndex:(NSInteger)index;</code>

<code>// (button, gesture, notification)等事件處理方法</code>
<code>#pragma mark - event handler</code>
<code>- (void)clickFavorBtnAction:(UIButton *)btn;</code>

<code>#pragma mark - if u want, put your custom markers here, or just write bellow 'methods'</code>
<code>#pragma mark - methods</code>

<code>#pragma mark - accessors</code>
<code>// 盡量使用懶加載方式構(gòu)建</code>
<code>- (UIButton)favorBtn</code>
{
if (_favorBtn == nil) {
<code> // initialize _favorBtn;</code>
}
return favorBtn;
}
</pre>
5.dispatch
*, block
優(yōu)先使用dispatch_after, dispatch_async, dispatch_sync等方法,替代原來的NSObject中的延遲執(zhí)行和在線程執(zhí)行處理等方法。
使用block注意通過weakSelf和strongSelf方式來避免循環(huán)引用,在block中引用self需要格外小心。
可以使用libextobjc開源庫中的@weakify(self) 和@strongify(self)(或者自己仿寫宏)(https://github.com/stapelberg/libxcb)
<pre>
//單例子的正常寫法(此處可以繼承我的一個單例抽象類)https://github.com/OrangeVarko/Singleton/tree/master/CLXSingleton
<code>+ (instancetype)sharedInstance
{</code>
<code> static id sharedInstance = nil;</code>
<code>static dispatch_once_t onceToken;</code>
<code>dispatch_once(&onceToken, ^{</code>
<code>sharedInstance = [[self alloc] init];</code>
});</code>
<code>return sharedInstance;</code>
}

// Good
<code>[UIView animateWithDuration:1.0 animations:^{</code>
<code>// something</code>
<code>} completion:^(BOOL finished) {</code>
<code>// something</code>
}];</code>

// Bad
<code>[UIView animateWithDuration:1.0</code>
<code>animations:^{</code>
<code>// something</code>
}
<code>completion:^(BOOL finished) {</code>
<code>// something</code>
}];

// Good
<code>__weak typeof(self) weakSelf = self;</code>
<code>self.request.onComplete = ^(id model) {</code>
<code>typeof(self) strongSelf = weakSelf;</code>
<code>[strongSelf hideProgressHud];</code>
...
}
//推薦這種
@weakify(self);
<code>self.request.onComplete = ^(id model) {</code>
<code>@strongify(self);//此處一定要</code>
<code>[strongSelf hideProgressHud];</code>
...
}

// Bad (self循環(huán)引用)
<code>self.request.onComplete = ^(id model) {</code>
<code> [self hideProgressHud];</code>
}
</pre>

  1. 代碼風格(換行,空格)

if else while等關(guān)鍵字與 ( 和 { 間加一個空格
雙目運算符(== &&等),注意兩邊空格, 單目運算符(! ++等)不要添加空格
對于較長的語句,注意換行,方法調(diào)用多參數(shù)方法調(diào)用注意將:對齊
反復(fù)使用的內(nèi)容,可以先用臨時變量存起來,減少語句長度。
<pre>
<code>- (BOOL)checkSomeFromCache:(NSArray *)adsTypes complete:(LoadSuccessBlock)complete </code>
{

<code> for (id i in expression) {</code>
<code>if (expression1 && expression2) {</code>
<code>//do something</code>
}
}
<code>while (expression1 == expression2) {</code>
<code> //do something</code>
}
}

// Good
// 下面多處使用,先臨時存儲
<code>NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];</code>

<code>NSString * lastestAlertTime = [userDefaults objectForKey:lastestAlertTimeKey];</code>
<code>NSNumber * currentShowCount = [userDefaults objectForKey:currentShowCountKey];</code>
<code>NSString * lastShowImg = [userDefaults objectForKey:imgKey];</code>
// ...
<code>[userDefaults setObject:@(currentShowCount.integerValue + 1) forKey:currentShowCountKey];</code>
<code>[userDefaults setObject:nowTime forKey:lastestAlertTimeKey];</code>
<code>[userDefaults setObject:dict[@"img"] forKey:imgKey];</code>
<code>[userDefaults synchronize];</code>

// Bad
<code>NSString * lastestAlertTime = [[NSUserDefaults standardUserDefaults] objectForKey:lastestAlertTimeKey];</code>
<code>NSNumber * currentShowCount = [[NSUserDefaults standardUserDefaults] objectForKey:currentShowCountKey];</code>
<code>NSString * lastShowImg = [[NSUserDefaults standardUserDefaults] objectForKey:imgKey];</code>
// ...
<code>[[NSUserDefaults standardUserDefaults] setObject:@(currentShowCount.integerValue + 1) forKey:currentShowCountKey];</code>
<code>[[NSUserDefaults standardUserDefaults] setObject:nowTime forKey:lastestAlertTimeKey];</code>
<code>[[NSUserDefaults standardUserDefaults] setObject:dict[@"img"] forKey:imgKey];</code>
<code>[[NSUserDefaults standardUserDefaults] synchronize];</code>
</pre>
8.日志
調(diào)試日志在測試完成后,統(tǒng)一換成DBLog(小件員自己寫的宏)等方式,防止在release版本中存在日志輸出
<pre>
<code>#ifndef Macro_h</code>
<code>#define Macro_h</code>
<code>//自定義日志函數(shù)</code>
<code>#ifdef DEBUG</code>

<code>#define DBLog(format, ...) do { </code>
<code> fprintf(stderr, "<%s : %d> %s\n",</code>
<code> [[[NSString stringWithUTF8String:FILE] </code><code>lastPathComponent] UTF8String], </code>
LINE, func);</code>
<code> (NSLog)((format), ##VA_ARGS); </code>
<code> fprintf(stderr, "-------\n"); </code>
<code>} while (0)</code>

<code>#else</code>

<code># define DBLog(...);</code>

<code>#endif</code>
</pre>

  1. try, catch
    減少try,catch的使用,可能造成ARC不work,盡量用其他方式實現(xiàn),除非你能保證發(fā)生異常時,所有對象都及時釋放。
    <pre>
    <code>- (void)exampleFunction {</code>
    ABMultiValueRef phone = ABRecordCopyValue(person, <code>kABPersonPhoneProperty);</code>
    long index = <code>ABMultiValueGetIndexForIdentifier(phone,identifier);</code>
    <code> try {</code>
    <code>NSString *phoneNO = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phone, index + 1);</code>
    <code>CFRelease(phone);//此處代碼執(zhí)行不到,直接跳到catch中了。導(dǎo)致內(nèi)存泄漏</code>
    <code> } catch {</code>
    <code>DBLog(@“數(shù)據(jù)越界異常”);</code>
    }
    }
    </pre>
  2. 需要盡量避免魔鬼數(shù)字出現(xiàn),可以通過正確的命名,enum,宏等提高代碼可閱讀性
    <pre>
    //good
    <code>typedef NS_ENUM(NSUInteger,BSTCountDownStatus)</code>
    {
    <code> BSTCountDownStatusCancelled,//時間未到,人為停止</code>
    <code>BSTCountDownStatusFinished,//計時完成</code>
    };
    <code>if (status == BSTCountDownStatusCancelled) {</code>
    //do..
    <code>} else if (status == BSTCountDownStatusFinished) {</code>
    //do..
    <code>} else {</code>
    //do..
    }

//bad
<code>if (status == 0) {</code>
//do..
<code>} else if (status == 1) {</code>
//do..
<code>} else {</code>
//do..
}
</pre>

  1. 盡量使用常量定義,避免宏定義
    <pre>
    //bad
    <code>#define KUserPhone @"userPhone"</code>
    //good
    <code>static NSString *const kUserPhone = @"userPhone";</code>
    </pre>
    12.采用提前return的方式,減少if語句的嵌套
    <pre>
    //good
    <code>- (void)viewDidAppear:(BOOL)animated</code>
    {
    <code> [super viewDidAppear:animated];</code>
    <code>if (self.URLRequest == nil) return;</code>
    <code>[self.webView loadRequest:self.URLRequest];</code>
    }
    //bad
    <code>- (void)viewDidAppear:(BOOL)animated</code>
    {
    <code> [super viewDidAppear:animated];</code>
    <code> if (self.URLRequest) {</code>
    <code> [self.webView loadRequest:self.URLRequest];</code>
    }
    }
    </pre>
    13.NSTimer潛在的循環(huán)引用
    <pre>
    <code>//.h</code>
    <code>@property (nonatomic, strong) NSTimer *timer;</code>
    <code>//.m</code>
    <code>self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(printTest) userInfo:nil repeats:YES];</code>
    <code>//-------------------------------------------------------</code>
    <code>//自己封裝NSTimer 以block的形式消除循環(huán)引用</code>
    <code>@weakify(self);</code>
    <code>self.timer = [NSTimer BST_scheduledTimerWithTimeInterval:1 block:^(NSTimer *timer) {</code>
    <code> @strongify(self);</code>
    <code> [self printTest];</code>
    <code>NSLog(@"BST_scheduledTimer");</code>
    <code>} repeats:YES];</code>
    </pre>
    14.通過category擴充原來對象,利用Associated Objects動態(tài)添加屬性
    <pre>
    <code>@interface UIView (MIAdditions)</code>

<code>@property (nonatomic, strong) NSObject *customData;</code>

<code>@end</code>

<code>@implementation UIView (MIAdditions)</code>

<code>- (NSObject *)customData</code>
{
<code>return objc_getAssociatedObject(self, @selector(customData));</code>
}

<code>- (void)setCustomData:(NSObject *)data</code>
{
<code>objc_setAssociatedObject(self, @selector(customData), data, OBJC_ASSOCIATION_RETAIN_NONATOMIC);</code>
}

@end
</pre>
15.對model中的字典和數(shù)組,在取值前多用runtime里面的類型自省方法: [xx isKindOfClass:[XX class]],[xx isMemberOfClass:[XX class]]
<pre>
<code>//good</code>
<code>//真正做打電話請求</code>
<code>- (void)requestServiceMakeCall</code>
{
<code> //@note:后端把主叫和被叫人接口字段弄反了</code>
<code> [SetPhoneNumService makeDoubleCallWith:self.callingPhone calling:self.calledPhone billCode:self.calledBillCode success:^(id response) {</code>
<code> if (!response) return;</code>

    <code>NSString * const codeKey = @"code";</code>
    <code>APPCallFeedBackStatus feedBack = APPCallFeedBackStatusCallFailed;</code>
   <code> if ([response[codeKey] isKindOfClass:[NSNumber class]]) {</code>
      <code>  NSNumber *status = response[codeKey];</code>
       <code> [self saveCallRecordWithStatus:status];</code>
       <code> feedBack = status.integerValue == 0 ?  </code> <code>APPCallFeedBackStatusCallSuceess : </code><code>APPCallFeedBackStatusCallFailed;</code>
   <code>}</code>
  <code>  [self dismissWithStatus:feedBack];</code>

<code> } fail:^(NSString *error, NSInteger statusCode) {</code>
<code> [self feedBackWithStatus:APPCallFeedBackStatusNetWorkError];</code>
<code>}];</code>
<code>}</code>
</pre>
</p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內(nèi)容