前言
工作很多年了,如今再來熟悉回顧一下 iOS 一些基礎(chǔ)的知識(shí)點(diǎn),查缺補(bǔ)漏,也可以規(guī)范、優(yōu)化自己的代碼,重拾一些容易被我們忽略掉的地方,改掉一些不良的代碼習(xí)慣,所以學(xué)習(xí)分享一下這本《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》。
此篇主要學(xué)習(xí)第一章:熟悉 Objective-C。這一章分為了 5 條有效方法,本章主要講基礎(chǔ)知識(shí)。
第1條:了解 Objective-C 語(yǔ)言的起源
OC 使用的是“消息結(jié)構(gòu)”,而非 ”函數(shù)調(diào)用“。OC 是由 Smalltalk 演化而來的,后者是消息型語(yǔ)言的鼻祖。
- 消息結(jié)構(gòu):
Object *obj = [Object new];
[obj preformWith:parameter1 and: parameter2];
- 函數(shù)調(diào)用:
Object *obj = new Object;
obj->perform(parameter1, parameter2);
區(qū)別:
消息結(jié)構(gòu):運(yùn)行時(shí)所執(zhí)行的代碼由運(yùn)行環(huán)境來決定。
函數(shù)調(diào)用:運(yùn)行時(shí)所執(zhí)行的代碼由編譯器決定。
OC 為 C 語(yǔ)言添加了面向?qū)ο筇匦?,是其超集?strong>OC 使用動(dòng)態(tài)綁定的消息結(jié)構(gòu),運(yùn)行時(shí)才會(huì)檢查對(duì)象類型。接收一條消息以后,最終執(zhí)行何種代碼,由運(yùn)行環(huán)境而非編譯器決定。
第2條:在類的頭文件中盡量少引入其他頭文件
將引入頭文件的時(shí)機(jī)盡量延后,只在確有需要時(shí)才引入,這樣可減少類的使用者所需引入頭文件數(shù)量,減少編譯時(shí)間。
// EOCPerson.h
#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, strong) EOCEmployer *employer;
上面添加了 EOCEmployer,一般是在.h中加入
#import "EOCEmployer.h"
這樣不夠優(yōu)雅,因?yàn)榫幾g EOCPerson 類時(shí)不需要知道EOCEmployer 類的全部細(xì)節(jié),只需要知道一個(gè)類名叫 EOCEmployer 就好,所以可以這樣告訴編譯器:
@class EOCEmployer;
這就叫 向前聲明,然后在 .m 中則需要引入 EOCEmployer 的頭文件了,因?yàn)橐褂煤笳弑仨氈榔渌薪涌诩?xì)節(jié)。
??總結(jié):
- 除非有必要,否則不要引入頭文件。應(yīng)在.h中采用向前聲明 ”@“ 來提及背的類,并在 .m 中引入,這樣可降低類間耦合。
- 無(wú)法向前聲明時(shí),比如聲明某個(gè)類遵循的協(xié)議時(shí)。盡量把協(xié)議聲明移到分類中,如果不行,就把協(xié)議單獨(dú)放在一個(gè)頭文件中再引入。
第3條:多用字面量語(yǔ)法,少用與之等價(jià)的方法
字面量語(yǔ)法實(shí)際上是一種 ”語(yǔ)法糖“(syntacitc sugar)。
- 糖衣語(yǔ)法:
是指計(jì)算機(jī)語(yǔ)言中與另外一套語(yǔ)法等效但是開發(fā)者用起來卻更加方便的語(yǔ)法??闪畛绦蚋鬃x,減少代碼出錯(cuò)幾率。
字面數(shù)值:
NSNumber *intNumber = [NSNumber numberWithInt: 1];
用字面量書法更整潔:
NSNumber *intNumber = @1;
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = YES;
NSNumber *charNumber = @'a';
// 字面量也適用于下面的表達(dá)式
int x = 5;
float y = 6.32f;
NSNumber *expressionNumber = @(x * y);
字面量數(shù)組:
NSArray *animals = @[@"cat", @"dog", @"mouse"];
NSString *dog = animals[1];
id object1 = [[NSObject alloc] init];
id object2 = nil;
id object3 = [[NSObject alloc] init];
NSArray *arrayA = [NSArray arrayWithObjects:object1, object2, object3, nil];
NSArray *arrayB = @[object1, object2, object3];
運(yùn)行結(jié)果是,arrayB 拋出異常,arrayA 創(chuàng)建成功,但只包含 object1 一個(gè)對(duì)象。
所以表明使用字面量語(yǔ)法更為安全,拋出異常崩潰比創(chuàng)建好數(shù)組才發(fā)現(xiàn)少了個(gè)元素要好,可以更快速發(fā)現(xiàn)錯(cuò)誤。
字面量字典:
NSDictionary *personData = @{@"firstName" : @"Matt",
@"lastName" : @"Galloway", @"age" : @28};
NSString *lastName = personData[@"lastName"];
可變數(shù)組與字典:
取下標(biāo)操作:
mutableArray[1] = @"dog";
mutableDictionary[@"lastName"] = @"Galloway";
局限性:
- 除了字符串以外,所創(chuàng)建出來的對(duì)象必須屬于 Foundation 框架才行。
- 字面量語(yǔ)法創(chuàng)建出來的字符串、數(shù)組、字典對(duì)象都是不可變的,若要可變版本的對(duì)象,需復(fù)制一份:
NSMutableArray *mutable = [@[@1, @2, @3] mutableCopy];
第4條:多用類型常量,少用 #define 預(yù)處理指令
#define ANIMATTION_DURATION 0.3
#define 的缺點(diǎn)是定義出來的常量沒有類型信息,并且如果是在某個(gè)頭文件中,那么所有引入此頭文件的代碼,其 ANIMATTION_DURATION 都會(huì)被替換為 0.3。
在頭文件中使用很容易造成沖突。
最好使用下面的方式:
static const NSTimeIntercal kAnimationDuration = 0.3;
- 常量常用的命名法:
如果只想在某個(gè)實(shí)現(xiàn)(.m)內(nèi)使用,則在前面加字母 k,如kAnimationDuration;
若常量在類之外可見,則通常用類名為前綴,如 EOCViewAnimationDuration。
用此方法定義的常量包含類型信息,清楚的描述了常量的含義。
變量一定要同時(shí)用 static 和 const 來聲明。這樣試圖修改 const 修飾的變量編譯器會(huì)報(bào)錯(cuò)。static 代表該變量?jī)H在定義此變量的編譯單元中可見(即 .m 中)。
如果不加 static,編譯器會(huì)創(chuàng)建一個(gè)”外部符號(hào)“,此時(shí)如果其他 .m 中聲明了同名變量,則編譯器會(huì)報(bào)錯(cuò)。
不打算公開某個(gè)常量時(shí),應(yīng)該定義在實(shí)現(xiàn)文件里。
如果打算公開某個(gè)常量:
在頭文件 .h 中聲明:
extern NSString *const EOCStringConstant;
在實(shí)現(xiàn)文件 .m 中定義:
NSString *const EOCStringConstant = @”VALUE“;
extern 關(guān)鍵字是要告訴編譯器,在全局符號(hào)表中將會(huì)有一個(gè)名叫 EOCStringConstant 的符號(hào)。編譯器無(wú)須查看其定義,即允許代碼使用此常量,因?yàn)殒溄映啥M(jìn)制文件后,肯定能找到這個(gè)常量。
??總結(jié):
- 不用
#define定義常量。1 是因?yàn)椴缓愋托畔ⅲ?strong>2 是因?yàn)槿绻腥藳_定義了常量值也不會(huì)警告。- 在 .m 里用
static const定義只在 .m 里可見的常量。- 在 .h 里用
extern聲明全局常量,并在對(duì)應(yīng)的 .m 中定義其值。這種常量出現(xiàn)在全局符號(hào)表中,所以名字要加類名前綴區(qū)分。
第5條:用枚舉表示狀態(tài)、選項(xiàng)、狀態(tài)碼
在以一系列常量來表示錯(cuò)誤狀態(tài)碼或可組合的選項(xiàng)時(shí),極宜使用枚舉為其命名。
枚舉是一種常量命名方式。某個(gè)對(duì)象所經(jīng)歷的各狀態(tài)可定義為一個(gè)枚舉集,如”套接字鏈接“的狀態(tài):
enum EOCConnectionState {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
}
typedef enum EOCConnectionState EOCConnectionState;
定義選項(xiàng)時(shí)也可使用枚舉。若這些選項(xiàng)可以彼此組合則更應(yīng)如此。各選項(xiàng)間可通過”按位或操作符“來組合。如下 UI 框架中的枚舉類型,表示某視圖該如何在水平或垂直方向上調(diào)整大小。
enum UIViewAutoresizing {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleLeftWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5,
}
enmu UIViewAutoresizing resizing = UIViewAutoresizingFlexibleLeftWidth | UIViewAutoresizingFlexibleHeight;
輔助宏 NS_ENUM 和 NS_OPTIONS。
typedef NS_ENUM(NSUInteger, EOCConnectionState) {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
}
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectionDown = 1 << 1,
EOCPermittedDirectionLeft = 1 << 2,
EOCPermittedDirectionRight = 1 << 3,
}
凡是需要以按位或操作來組合的枚舉都應(yīng)該使用 NS_OPTIONS,若是枚舉不需要互相組合,則應(yīng)使用 NS_ENUM 來定義。
??總結(jié):
- 應(yīng)用枚舉表示狀態(tài)、選項(xiàng)、狀態(tài)碼、樣式等值,起個(gè)易懂的名。
- 如果多個(gè)選項(xiàng)可同時(shí)使用,可定義為2的冪形式,以便通過按位或操作符組合。
- 用
NS_OPTIONS和NS_ENUM來定義枚舉類型,并指明底層數(shù)據(jù)類型。- 處理枚舉類型的 switch 中不要實(shí)現(xiàn) default 分支。不然加了新枚舉后會(huì)有黃色警告。
第一章就學(xué)習(xí)總結(jié)到這里,后面的請(qǐng)看后續(xù)的文章。
以上的總結(jié)參考了并部分摘抄了以下文章,非常感謝以下作者的分享?。?br> 《編寫高質(zhì)量iOS與OS X代碼的52個(gè)有效方法》
轉(zhuǎn)載請(qǐng)備注原文出處,不得用于商業(yè)傳播——凡幾多