《Effective Objective-C 2.0》之《熟悉Objective-C》讀書筆記

之前閱讀過(guò)《Effective Objective-C 2.0》废岂,覺(jué)得有些知識(shí)點(diǎn)忘記了,在此再把每個(gè)章節(jié)的內(nèi)容都整理一遍

了解Objective-C語(yǔ)言的起源

大家可能知道狱意,Objective-C語(yǔ)言使用的是消息結(jié)構(gòu)
那我們看下消息與函數(shù)調(diào)用的卻別湖苞,看上去就像這樣:

//Messageing (Objective-C)
Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];

//Function calling (C++)
Object *obj = new Object;
obj->perform(parameter1, parameter2);

關(guān)鍵區(qū)別在于:使用消息結(jié)構(gòu)的語(yǔ)言,其運(yùn)行時(shí)所應(yīng)執(zhí)行的代碼由運(yùn)行環(huán)境來(lái)決定详囤;而使用函數(shù)調(diào)用的語(yǔ)言财骨,則由編譯器決定。如果范例代碼中調(diào)用的函數(shù)是多態(tài)的藏姐,那么運(yùn)行時(shí)就要按照“虛方法表”(virtual table)來(lái)查出到底應(yīng)該執(zhí)行哪個(gè)函數(shù)實(shí)現(xiàn)隆箩。而采用消息結(jié)構(gòu)的語(yǔ)言,不論是否多態(tài)羔杨,總是運(yùn)行時(shí)才會(huì)去查找所要執(zhí)行的方法捌臊。
Objective-C的重要工作是由“運(yùn)行期組件”(runtime component)而非編譯器來(lái)完成的。使用Objective-C的面向?qū)ο筇匦运璧娜繑?shù)據(jù)結(jié)構(gòu)以及函數(shù)都在運(yùn)行期組件里面兜材。舉例來(lái)說(shuō)理澎,運(yùn)行期組件中含有全部?jī)?nèi)存管理方法。運(yùn)行期組件本質(zhì)上就是一種與開(kāi)發(fā)者所編代碼相鏈接的“動(dòng)態(tài)庫(kù)”(dynamic library),其代碼能把開(kāi)發(fā)者編寫的所有程序粘合起來(lái)曙寡。這樣的話糠爬,只需要更新運(yùn)行期組件,即可提升應(yīng)用程序性能举庶。而那種許多工作都在“編譯器”(compile time)完成的語(yǔ)言执隧,若想要獲得類似的性能提升,則要重新編譯應(yīng)用程序代碼灯变。

要點(diǎn)
  • Objective-C為C語(yǔ)言添加了面向?qū)ο筇匦耘孤辏瞧涑bjective-C使用動(dòng)態(tài)綁定的消息結(jié)構(gòu)添祸,也就是說(shuō)滚粟,在運(yùn)行時(shí)才會(huì)檢查對(duì)象類型。接受一條消息之后刃泌,究竟應(yīng)執(zhí)行何種代碼凡壤,由運(yùn)行期環(huán)境而非編譯器決定署尤。
  • 理解C語(yǔ)言的核心概念有助于寫好Objective-C程序。尤其要掌握內(nèi)存模型與指針亚侠。

在類的頭文件中盡量少引入其他頭文件

要點(diǎn)
  • 除非確有必要曹体,否則不要引入頭文件。一般來(lái)說(shuō)硝烂,應(yīng)在某個(gè)類的頭文件中使用向前聲明來(lái)提及類別的類箕别,并在實(shí)現(xiàn)文件中引入那些類的頭文件。這樣做可以盡量降低類之間的耦合(coupling)滞谢。
  • 有時(shí)無(wú)法使用向前聲明串稀,比如要聲明某個(gè)類遵循一項(xiàng)協(xié)議。這種情況下狮杨,盡量把“該類遵循某協(xié)議”的這條聲明移至“class-continuation分類”中母截。如果不行的話,就把協(xié)議單獨(dú)放在頭文件中橄教,然后將其引入清寇。
什么是向前聲明

在編譯一個(gè)使用了ClassA類的文件時(shí),不需要知道ClassA類的全部實(shí)現(xiàn)細(xì)節(jié)护蝶,只需要知道有一個(gè)類名叫ClassA就好华烟。如下代碼:

#import <UIKit/UIKit.h>

@class ClassA;

@interface ClassB : NSObject

@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, strong) ClassA *classA;
@end

其中@class ClassA就是向前聲明

將引入頭文件的時(shí)機(jī)盡量延后,只在確有需要時(shí)才引入滓走,這樣就可以減少類的使用者所需引入的頭文件數(shù)量垦江。如果直接引入頭文件,則可能要引入許多根本用不到的內(nèi)容搅方,從而增加編譯時(shí)間比吭。
同時(shí),向前聲明也解決了兩個(gè)類相互引用的問(wèn)題姨涡。假設(shè)要為ClassB類中添加新增以及刪除ClassA的方法衩藤,那么其頭文件中會(huì)加入下述定義:

- (void)addClassA:(ClassA *)classA;
- (void)removeClassA:(ClassA *)classA;

此時(shí),若要編譯ClassB涛漂,則編譯器必須知道ClassA這個(gè)類赏表,而要編譯ClassA,則又必須知道ClassB匈仗。如果在各自頭文件中引入對(duì)方的頭文件瓢剿,則會(huì)導(dǎo)致“循環(huán)引用”(chicken-and-egg situation)。當(dāng)解析其中一個(gè)頭文件時(shí)悠轩,編譯器會(huì)發(fā)現(xiàn)它引入了另一個(gè)頭文件间狂,而那個(gè)頭文件又回過(guò)來(lái)引用第一個(gè)頭文件。使用#import而非#include指令雖然不會(huì)導(dǎo)致死循環(huán)火架,但這意味著兩個(gè)類里有一個(gè)無(wú)法被正確編譯鉴象。如果不信的話忙菠,讀者可以自己試試。
但是有的時(shí)候必須要將頭文件中引入其他頭文件纺弊。

  1. 如果你寫的類繼承自某個(gè)超類牛欢,則必須引入定義那個(gè)超類的頭文件。
  2. 同理淆游,如果要聲明某個(gè)類遵循某個(gè)協(xié)議(protocol)傍睹,那么該協(xié)議必須有完整定義,且不能使用向前聲明稽犁。

向前聲明只能告訴編輯器有個(gè)某個(gè)協(xié)議焰望,而此時(shí)編譯器卻要知道該協(xié)議中定義的方法骚亿。
例如已亥,要從圖形類中繼承一個(gè)矩形類,且令其遵循繪制協(xié)議:

// EOCRectangle.h

#import "EOCShape.h"
#import "EOCDrawable.h"

@interface EOCRectangle : EOCShape<EOCDrawable>
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat height;
@end

第二條#import是難免的来屠。鑒于此虑椎,最好把協(xié)議單獨(dú)放在一個(gè)頭文件中。要是把EOCDrawable協(xié)議放在了某個(gè)大的頭文件里俱笛,那么只要引入此協(xié)議捆姜,就必定會(huì)引入那個(gè)頭文件中的全部?jī)?nèi)容,如此一來(lái)迎膜,就像上面說(shuō)的那樣泥技,會(huì)產(chǎn)生相互依賴問(wèn)題,而且還會(huì)增加編譯時(shí)間磕仅。

對(duì)于沒(méi)有必要協(xié)議暴露出來(lái)的情況珊豹,可以將協(xié)議放在“class-continuation分類”中。這樣的話榕订,只要在實(shí)現(xiàn)文件中引入包含委托協(xié)議的頭文件即可店茶,而不需要將其放在公共頭文件里。如下代碼:

// EOCRectangle.m

#import "EOCDrawable.h"

@interface EOCRectangle ()<EOCDrawable>

@end

此時(shí)劫恒,協(xié)議EOCDrawable就沒(méi)有暴露在頭文件中贩幻,可以避免相互依賴和增加編譯時(shí)間的問(wèn)題

多用字面量語(yǔ)法,少用與之等價(jià)的方法

要點(diǎn):
  • 應(yīng)該使用字面量語(yǔ)法來(lái)創(chuàng)建字符串两嘴、數(shù)值丛楚、數(shù)組、字典憔辫。與創(chuàng)建此類對(duì)象的常規(guī)方法相比趣些,那么做更加簡(jiǎn)明扼要。
  • 應(yīng)該通過(guò)取下標(biāo)操作來(lái)訪問(wèn)數(shù)組下標(biāo)或字典中的鍵所對(duì)應(yīng)的元素螺垢。
  • 用字面量語(yǔ)法創(chuàng)建數(shù)組或字典時(shí)喧务,若值中有nil赖歌,則會(huì)拋出異常。因此功茴。務(wù)必確保值不含nil庐冯。
字面量語(yǔ)法
//字符串
 NSString *someString = @"Effective Obejctive-C 2.0";
//數(shù)值
NSNumber *intNumber = @1;
NSNumber *floatNumber = @1.5f;
NSNumber *doubleFloatNumber = @1.5f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';   
//數(shù)組
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
//字典 
NSDictionary *personData = @{@"firstNmae":@"Matt", @"lastName":@"Galloway", @"age":@28};
字面量語(yǔ)法取值
//數(shù)值
NSArray *animals = @[@"cat", @"dog", @"mouse", @"badger"];
NSString *dog = animals[1];
//字典
NSDictionary *personData = @{@"firstNmae":@"Matt", @"lastName":@"Galloway", @"age":@28};
NSString *lastName = personData[@"lastName"];```
#####注意點(diǎn)
數(shù)組和字典使用字面量語(yǔ)法來(lái)初始化時(shí),如果其他包含nil元素坎穿,會(huì)導(dǎo)致crash
#####局限性
使用字面量語(yǔ)法創(chuàng)建的對(duì)象為不可變的(immutable)展父,如果需要獲得可變對(duì)象,需要復(fù)制一份玲昧,如下代碼:

NSMutableArray *mutable = @[@1, @2, @3, @4, @5].mutableCopy;


####多用類型變量栖茉,少用#define預(yù)處理命令
#####要點(diǎn)
* 不要用預(yù)處理指令定義常量。這樣定義出來(lái)的常量不含類型信息孵延,編譯器只會(huì)在編譯前據(jù)此執(zhí)行查找與替換操作吕漂。即使有人重新定義了常量值,編譯器也不會(huì)產(chǎn)生警告信息尘应,這將導(dǎo)致應(yīng)用程序中的常量不一致惶凝。
* 在實(shí)現(xiàn)文件中使用static const來(lái)定義“只在編譯編譯單元內(nèi)可見(jiàn)的常量”(translation-unit-specific constant)。由于此類常量不在全局符號(hào)表中犬钢,所以無(wú)需為其名稱加前綴苍鲜。
* 在頭文件中使用extern來(lái)聲明全局常量,并在相關(guān)實(shí)現(xiàn)文件中定義其值玷犹。這種常量要出現(xiàn)在全局符號(hào)表中混滔,所以其名稱應(yīng)加以區(qū)隔,通常用與之相關(guān)的類名做前綴歹颓。

比如坯屿,定義一個(gè)動(dòng)畫時(shí)間常量

//不應(yīng)該使用

define ANIMATION_DURATION 0.3

//應(yīng)該使用
static const NSTimeInterval kAnimationDuration = 0.3;```

還要注意常量名稱。常用的命名是:若變量局限于某“編譯單元”(translation unit晴股,也就是“實(shí)現(xiàn)文件”愿伴,implement file)之內(nèi),則在前面加字母k电湘;若常量在類之外可見(jiàn)隔节,則通常以類名為前綴。
若局限于 實(shí)現(xiàn)文件內(nèi)寂呛,則可以用以上代碼怎诫,若作為全局變量,為了防止類名沖突贷痪,命名應(yīng)該添加類名前綴幻妓,如下所示:

//EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;

//EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;```
此時(shí)作為全局變量,不應(yīng)該添加static來(lái)修飾劫拢。而且需要在頭文件中聲明該變量肉津,添加extern前綴强胰,這個(gè)可以參照C語(yǔ)言的語(yǔ)法。
這樣定義常量對(duì)于使用#define預(yù)處理命令來(lái)說(shuō)妹沙,可以確保常量值不變偶洋。
####用枚舉表示狀態(tài)、選線距糖、狀態(tài)碼
#####要點(diǎn)
* 應(yīng)該用枚舉來(lái)表示狀態(tài)機(jī)的狀態(tài)玄窝、傳遞給方法的選項(xiàng)以及狀態(tài)碼等值,給這些值起個(gè)易懂的名字悍引。
* 如果傳遞給某個(gè)方法的選項(xiàng)表示為枚舉類型恩脂,而多個(gè)選項(xiàng)又可以同時(shí)使用,那么就將各選項(xiàng)值定義為2的冪趣斤,以便通過(guò)按位或操作將其組合起來(lái)俩块。
* 用NS_ENUM于NS_OPTIONS宏來(lái)定義枚舉類型,并指明底層數(shù)據(jù)類型唬渗。這樣做可以確保枚舉是用開(kāi)發(fā)者所選的底層數(shù)據(jù)類型實(shí)現(xiàn)出來(lái)的典阵,而不會(huì)采用編譯器所選的類型。
* 在處理枚舉類型的switch語(yǔ)句中不要實(shí)現(xiàn)default分支镊逝。這樣的話,加入新枚舉之后嫉鲸,編譯器就會(huì)提示開(kāi)發(fā)者:switch語(yǔ)句并未處理所有枚舉撑蒜。

#####樣例

//枚舉
typedef NS_ENUM(NSUInteger, EOCConnectionState) {
EOCConnectionStateDisconnected,
EOCConnectionStateConnecting,
EOCConnectionStateConnected,
};
//枚舉使用switch
EOCConnectionState state = EOCConnectionStateDisconnected;
switch (state) {
case EOCConnectionStateDisconnected:
//Disconnected
break;
case EOCConnectionStateConnected:
//Connected
break;
case EOCConnectionStateConnecting:
//Connecting
break;
}
//選項(xiàng),可以用來(lái)組合的枚舉
typedef NS_OPTIONS(NSUInteger, EOCPermittedDirection) {
EOCPermittedDirectionUp = 1 << 0,
EOCPermittedDirectionDown = 1 << 1,
EOCPermittedDirectionLeft = 1 << 2,
EOCPermittedDirectionRight = 1 << 2,
};
//選項(xiàng)使用方法
EOCPermittedDirection direction = EOCPermittedDirectionUp | EOCPermittedDirectionDown;
if (direction & EOCPermittedDirectionUp) {
//Direction is up
}
if (direction & EOCPermittedDirectionDown) {
//Direction is down
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末玄渗,一起剝皮案震驚了整個(gè)濱河市座菠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藤树,老刑警劉巖浴滴,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異岁钓,居然都是意外死亡升略,警方通過(guò)查閱死者的電腦和手機(jī)屡限,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钧大,“玉大人,你說(shuō)我怎么就攤上這事啊央≌谴祝” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵东帅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我靠闭,道長(zhǎng),這世上最難降的妖魔是什么坎炼? 我笑而不...
    開(kāi)封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮谣光,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萄金。我一直安慰自己,他們只是感情好氧敢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布日戈。 她就那樣靜靜地躺著,像睡著了一般孙乖。 火紅的嫁衣襯著肌膚如雪浙炼。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天唯袄,我揣著相機(jī)與錄音弯屈,去河邊找鬼。 笑死恋拷,一個(gè)胖子當(dāng)著我的面吹牛资厉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔬顾,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宴偿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了阎抒?” 一聲冷哼從身側(cè)響起酪我,我...
    開(kāi)封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎且叁,沒(méi)想到半個(gè)月后都哭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年欺矫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纱新。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡穆趴,死狀恐怖脸爱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情未妹,我是刑警寧澤簿废,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站络它,受9級(jí)特大地震影響族檬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜化戳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一单料、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧点楼,春花似錦扫尖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至却盘,卻和暖如春狰域,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背黄橘。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工塞关, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留子巾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓椰于,卻偏偏與公主長(zhǎng)得像瘾婿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抢呆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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