<<Effective Objective -C 2.0>>閱讀筆記只是作為騷棟的自己的心得,建議各位看官自己去看<<Effective Objective -C 2.0>>,五星推薦,推薦理由:面試儲(chǔ)備必備含友、內(nèi)存性能優(yōu)化吟逝、提高代碼質(zhì)量.非廣告~
前言
<<Effective Objective -C 2.0>>只是做閱讀的筆記,所有的筆記只是把52個(gè)相關(guān)有效方法進(jìn)行總結(jié)以及闡述騷棟的觀點(diǎn).<<Effective Objective -C 2.0>>這本書對(duì)實(shí)際開發(fā)過(guò)程中還是有著很大幫助的,尤其是項(xiàng)目的性能優(yōu)化方面有著巨大的幫助,而且如果你想當(dāng)一個(gè)iOS面試官,或者說(shuō)你要去面試iOS,那么<<Effective Objective -C 2.0>>這本書我推薦給你.我將按照讀書的每一個(gè)方法進(jìn)行記錄.看官們可以自行查看.
熟悉Objective-C(第一章標(biāo)題)
全書總共分為7個(gè)章節(jié),這一篇文章,我們主要說(shuō)一下第一章節(jié)熟悉Object-C的內(nèi)容.這一章節(jié)總共有五條建議,下面我們一一道來(lái).
-
了解 Objective-C的起源
這個(gè)前面就說(shuō)了一堆關(guān)于OC的家族史,我們就暫時(shí)跳過(guò)了,但是我們要說(shuō)說(shuō)這個(gè)OC中獨(dú)有的"消息結(jié)構(gòu)",其實(shí)我更喜歡稱之為"消息發(fā)送機(jī)制".首先從語(yǔ)法外表上來(lái)說(shuō),相比于C語(yǔ)言的"函數(shù)調(diào)用"和其他語(yǔ)言例如java,js中通過(guò)調(diào)用某一個(gè)對(duì)象中的某一個(gè)方法都是使用"點(diǎn)語(yǔ)法".而OC中使用的是一個(gè)[]
來(lái)表明某一個(gè)對(duì)象調(diào)用了某一個(gè)方法.如下示例所示.那么從本質(zhì)上來(lái)說(shuō),OC語(yǔ)言是總是在運(yùn)行的時(shí)候才去查找所需要執(zhí)行的方法.(可以查看<<Effective Objective -C 2.0>>補(bǔ)充(一):OC中的消息機(jī)制(轉(zhuǎn)載))
NewObject *object = [[NewObject alloc]init];
[object action];
另外這一部分還說(shuō)了一個(gè)名詞,那就是指針,指針是什么?有一本書的名字詮釋指針的定義,叫做"指針是C語(yǔ)言中一個(gè)閃亮的星星",如果C語(yǔ)言中什么最重要,那么無(wú)疑是三樣,一是指針,二是鏈表,三是結(jié)構(gòu)體.在OC中我們到處可見的就是*
,作為指針,OC中沒有想C中那么要求嚴(yán)格了,但是這里有一個(gè)問(wèn)題,可能連作為資深的你都不知道,那就是 我們都知道OC中的對(duì)象是存儲(chǔ)在堆區(qū)的,那么作為指向?qū)ο蟮刂返闹羔樖谴鎯?chǔ)在什么區(qū)?難道也是堆區(qū)?,正確答案是棧區(qū).
OC作為C語(yǔ)言的超集,添加了面向?qū)ο蟮奶匦?作為一個(gè)iOS開發(fā)人員,我們使用對(duì)象那真是到了如火純青的地步了.在OC中我們也會(huì)經(jīng)常用到結(jié)構(gòu)體担平,比如CGRect,在C語(yǔ)言中結(jié)構(gòu)體的存在意義類似于OC中的對(duì)象闲勺。下面我們就比較一下兩者的區(qū)別。
首先钱磅,結(jié)構(gòu)體是存儲(chǔ)于內(nèi)存的棧區(qū)的式撼,而OC中的對(duì)象則是存儲(chǔ)在內(nèi)存的堆區(qū);再者告匠,C語(yǔ)言中的結(jié)構(gòu)體要比OC的對(duì)象中對(duì)內(nèi)存性能的影響更小戈抄。主要是原因是在于OC對(duì)象的創(chuàng)建需要額外的開銷,比如分配及釋放堆內(nèi)存等后专。
-
在類的頭文件中盡量少引入其他頭文件
我們?cè)谝粋€(gè)類中經(jīng)常會(huì)使用到另外的一個(gè)類,那么我通常需要到導(dǎo)入頭文件,通常我們導(dǎo)入使用#import來(lái)導(dǎo)入頭文件,我們看下面一種情況,我們需要把NewObject
這個(gè)類的對(duì)象作為ViewController
的一個(gè)屬性,而且需要暴露出來(lái),所以把屬性寫在View Controller
的頭文件中.我們可能就是用#import "NewObject.h"
來(lái)導(dǎo)入到頭文件中.如下所示.
#import <UIKit/UIKit.h>
#import "NewObject.h"
@interface ViewController : UIViewController
@property(nonatomic,strong)NewObject* obj;
@end
雖然這樣是可行,但是卻不夠優(yōu)雅,在編譯的時(shí)候,我們只需要知道NewObject
這個(gè)類是存在的即可,我們不需要這個(gè)類的全部細(xì)節(jié),所以這里我們使用@class導(dǎo)入對(duì)象的頭文件即可.如下所示.
#import <UIKit/UIKit.h>
@class NewObject;
@interface ViewController : UIViewController
@property(nonatomic,strong)NewObject* obj;
@end
那么這么做到底有什么好處呢?或者說(shuō),這么做到底有什么樣的優(yōu)勢(shì)呢?使用@class將引入頭文件的時(shí)機(jī)盡量延后,只在確有需要時(shí)才引入,這樣就可以減少類的使用者所需引入的頭文件數(shù)量.假設(shè)使用#import "NewObject.h"
來(lái)導(dǎo)入到頭文件中,那么會(huì)一并引入NewObject的所有內(nèi)容.若此過(guò)程持續(xù)下去,則要引入許多根本用不到的內(nèi)容,會(huì)大大的增加編譯時(shí)間.另外使用@class還解決了另外的一個(gè)問(wèn)題,那就是循環(huán)引用問(wèn)題.下面我們就舉例說(shuō)明一下關(guān)于@class解決循環(huán)引用的示例.
我們假設(shè)有兩個(gè)類,分別叫FirstObject和SecondObject,在FirstObject中有一個(gè)SecondObject屬性對(duì)象,同時(shí)在SecondObject中有一個(gè)FirstObject屬性對(duì)象,同時(shí)我們都是使用#import導(dǎo)入頭文件,所以兩個(gè)類的情況就如下所示了.
#import <Foundation/Foundation.h>
#import "SecondObject.h"
@interface FirstObject : NSObject
@property(nonatomic,strong)SecondObject *sencondObject;
@end
#import <Foundation/Foundation.h>
#import "FirstObject.h"
@interface SecondObject : NSObject
@property(nonatomic,strong)FirstObject *firstObject;
@end
但是,這樣就會(huì)出現(xiàn)循環(huán)引用問(wèn)題,這是為什么呢?當(dāng)解析其中的一個(gè)頭文件的時(shí)候,編譯器會(huì)發(fā)現(xiàn)它引入另一個(gè)頭文件,而那個(gè)頭文件又回過(guò)頭來(lái)引入第一個(gè)頭文件,這樣反反復(fù)復(fù),會(huì)最終導(dǎo)致一個(gè)類無(wú)法被正確編譯.如下圖所示.
那么我們改如何解決這種循環(huán)引用問(wèn)題呢?我們只需要把兩個(gè)頭文件的任意一個(gè)導(dǎo)入換成@class方式即可,前面我們說(shuō)過(guò)@class只是說(shuō)明有這個(gè)類的存在,并不會(huì)導(dǎo)入這個(gè)類的全部信息.比如我們把FirstObject中的頭文件如下修改,然后就不會(huì)再出現(xiàn)編譯錯(cuò)誤了.
#import <Foundation/Foundation.h>
@class SecondObject;
@interface FirstObject : NSObject
@property(nonatomic,strong)SecondObject *sencondObject;
@end
-
多用字面量語(yǔ)法,少用與之等價(jià)的方法
其中,對(duì)于字面量語(yǔ)法,這幾年學(xué)習(xí)的iOS童鞋都會(huì)使用.這種字面量語(yǔ)法從Objective-C 1.0就開始出現(xiàn)了.時(shí)代已經(jīng)相當(dāng)久遠(yuǎn)了~對(duì)于從iOS 6走過(guò)來(lái)的我們來(lái)說(shuō),我們只需要知道如何使用即可,像NSString划鸽、NSArray、NSDictionary戚哎、NSNumber這種不可變類型的對(duì)象都可以使用字面量語(yǔ)法.示例如下所示.
NSString *string = @"字面量語(yǔ)法";
NSArray *array = @[@"棟哥",@"菜哥",@"狗哥"];
NSDictionary *dictionary = @{@"key":@"value"};
NSNumber *number = @1;
使用字面量語(yǔ)法,到底有什么優(yōu)勢(shì)呢?其實(shí)主要是更為簡(jiǎn)單易讀.我們看下面兩種創(chuàng)建方式.我們發(fā)現(xiàn)使用字面量語(yǔ)法創(chuàng)建字符串更加的簡(jiǎn)答易懂.
NSString *string = @"字面量語(yǔ)法";
NSString *string = [NSString stringWithString:@"字面量語(yǔ)法"];
而且,現(xiàn)在如何使用下面的那種方式創(chuàng)建,那么會(huì)出現(xiàn)警告.如下圖所示.
-
多用類型常量,少用#define預(yù)處理指令
在實(shí)際開發(fā)的過(guò)程中,我們經(jīng)常會(huì)使用預(yù)處理指令來(lái)定義我們常用的常量,例如我們定義一個(gè)int類型的常量,如下所示.
#define KLength 12
但是這樣使用預(yù)處理指令到底會(huì)出現(xiàn)什么弊端呢?其實(shí),整體上來(lái)說(shuō)還是規(guī)范的問(wèn)題,首先使用預(yù)處理指令沒有表明常量的類型信息,所有不能很好的表明的描述常量的定義;而且,預(yù)處理過(guò)程會(huì)把碰到的所有的KLength替換成12,這樣的話,假設(shè)此指令聲明在某個(gè)頭文件中,那么所有引入了這個(gè)頭文件的代碼,KLength都會(huì)被替換掉.
那么,我們?cè)撊绾谓鉀Q上面闡述的兩個(gè)問(wèn)題呢,我們可以使用以下的代碼來(lái)定義KLength這個(gè)常量.我們這樣定義之后,既解決了引入頭文件無(wú)故替換問(wèn)題,而且還包含常量的類型信息.
static const int KLength = 12;
-
用枚舉表示狀態(tài)裸诽、選項(xiàng)、狀態(tài)碼
使用枚舉來(lái)表示狀態(tài)應(yīng)該是一個(gè)很常見的使用,比如我以前寫過(guò)的SDLaunch:一個(gè)雜七雜八,卻功能完整的廣告引導(dǎo)頁(yè),SDLaunch的類型就有四種,我在使用過(guò)程中就是用了枚舉來(lái)表示不同的類型如下所示.
typedef enum {
ADLaunchViewController,//廣告類型
GreenhandLaunchViewController,//輪播圖新手導(dǎo)引類型
GifBackgroundLaunchViewController,//gif圖背景類型
RollImageLaunchViewController//滾動(dòng)圖片類型
} LaunchViewControllerType;
還有使用枚舉來(lái)表示選項(xiàng),這個(gè)是我們經(jīng)常在一些頭文件中看到的,用枚舉來(lái)表示選項(xiàng)和枚舉來(lái)表示狀態(tài)是不一樣的,一般我們用枚舉來(lái)表示狀態(tài)的時(shí)候是會(huì)選擇其中的一個(gè)枚舉值,而用枚舉來(lái)表示選項(xiàng)則是可能是多種多樣的選擇組合.換句話說(shuō)就是狀態(tài)是單選,而選項(xiàng)是多選的.
那么我們?nèi)绾问褂妹杜e來(lái)表示選項(xiàng)呢?如下所示.
//正確的選項(xiàng)枚舉示例
typedef enum {
firstType = 0,
secondType = 1<<0,
thirdType = 2<<0,
fourthType = 3<<0
}enumType;
這里我們看到了我們使用了移位符<<,首先說(shuō)明一下什么叫移位符.
位移位運(yùn)算符是將數(shù)據(jù)看成二進(jìn)制數(shù)型凳,對(duì)其進(jìn)行向左或向右移動(dòng)若干位的運(yùn)算丈冬。位移位運(yùn)算符分為左移和右移兩種,均為雙目運(yùn)算符啰脚。第一運(yùn)算對(duì)象是移位對(duì)象殷蛇,第二個(gè)運(yùn)算對(duì)象是所移的二進(jìn)制位數(shù)。
也就是說(shuō)1<<0的值為二級(jí)制的10,2<<0的值為二級(jí)制的100,那么為什么要這么做呢?我們假設(shè)不使用移位符,那么我們直接使用1,2,3,4來(lái)給枚舉賦值.如下所示.
//錯(cuò)誤的選項(xiàng)枚舉示例
typedef enum {
firstType = 0,
secondType = 1,
thirdType = 2,
fourthType = 3
}enumType;
假設(shè)我們需要是的thirdType和secondType兩個(gè)選項(xiàng),我們可以通過(guò)"按位或操作符"來(lái)組合.如下所示.但是這樣就真的沒有錯(cuò)誤了嗎?使用"按位或操作符"其實(shí)是組合兩個(gè)枚舉值的值,也就說(shuō)newType現(xiàn)在的值是3,那么跟enumType newType = fourthType;這樣是一個(gè)意義了,并不能表達(dá)選擇了多個(gè)枚舉值.
enumType newType = secondType|thirdType;
假設(shè)我們使用了移位符<<那一套的枚舉值就不會(huì)這樣的問(wèn)題了,我們來(lái)看一下這是為什么呢?我們還是使用** enumType newType = secondType|thirdType;這時(shí)候,newType的枚舉值是多少呢?是二進(jìn)制的110,如果說(shuō)enumType newType = fourthType;**,那么枚舉值也會(huì)發(fā)生改變?yōu)?000,兩者并不是相等的,這樣我們就區(qū)分開了.示例圖像如下所示.
和枚舉經(jīng)常配合使用的經(jīng)常的switch分支語(yǔ)句,我們知道switch分支語(yǔ)句在最后都會(huì)默認(rèn)的實(shí)現(xiàn)default語(yǔ)句,這里,書中不建議我們把這個(gè)分支進(jìn)行實(shí)現(xiàn),這樣的話,如果加入新枚舉之后,那么編譯器就會(huì)提醒開發(fā)者,switch并沒有處理所有的枚舉.
結(jié)束
關(guān)于<<Effective Objective -C 2.0>>中的第一章的五個(gè)注意點(diǎn)就寫到這里,由于本文為筆記形式,所以需要更深刻的理解書中內(nèi)容,還是建議讀者去自行閱讀<<Effective Objective -C 2.0>>這本書,下面發(fā)一個(gè)PDF的書籍傳送門.大家可以去看PDF版的,如果有任何問(wèn)題歡迎聯(lián)系騷棟,我們一同探討.謝謝大家.