級別: ★☆☆☆☆
標(biāo)簽:「iOS」「OC」「Objective-C」
作者: MrLiuQ
審校: QiShare團隊
前言:
這幾篇文章是小編在鉆研《Effective Objective-C 2.0》的知識產(chǎn)出,其中包含作者和小編的觀點,以及小編整理的一些demo。希望能幫助大家以簡潔的文字快速領(lǐng)悟原作者的精華。
在這里,QiShare團隊向原作者Matt Galloway表達誠摯的敬意。
文章目錄如下:
iOS 編寫高質(zhì)量Objective-C代碼(一)
iOS 編寫高質(zhì)量Objective-C代碼(二)
iOS 編寫高質(zhì)量Objective-C代碼(三)
iOS 編寫高質(zhì)量Objective-C代碼(四)
iOS 編寫高質(zhì)量Objective-C代碼(五)
iOS 編寫高質(zhì)量Objective-C代碼(六)
iOS 編寫高質(zhì)量Objective-C代碼(七)
iOS 編寫高質(zhì)量Objective-C代碼(八)
目前iOS開發(fā)主推的官方語言有兩種:Objective-C 和 Swift。
今天,小編幫助大家更加熟悉Objective-C,并且聊一聊如何才能編寫高質(zhì)量的OC代碼。
一、Objective-C的起源
談到Objective-C語言的出現(xiàn),可要比Java還要早十多年。
Java在1995年推出,而Objective-C早在1980年代就已經(jīng)出現(xiàn)了。
Objective-C (OC)由Smalltalk語言演化而來,后者是消息傳遞型語言的鼻祖。
-
消息傳遞?是的!引入了今天的第一個Key :消息傳遞。
OC與C++、Java等面向?qū)ο笳Z言類似,但又有很大區(qū)別。為什么這么說呢?首先要引入的話題就是OC使用消息傳遞機制,而并非C++、Java使用函數(shù)調(diào)用機制。
// Objective-C : messaging (消息傳遞)
Object *obj = [Object new];
[obj performWith:parameter1 and:parameter2];
// C++: function calling(函數(shù)調(diào)用)
Object *obj = new Object;
obj->perform(parameter1, parameter2);
區(qū)別:
消息傳遞:運行時所執(zhí)行的代碼由運行時環(huán)境決定(runtime)
函數(shù)調(diào)用:運行時所執(zhí)行的代碼由編譯器決定
簡單來說,OC總在運行時才會去查找真正所要調(diào)用的方法,編譯器并不用關(guān)心接收消息的對象是什么類型,接收消息的對象也是在運行時才工作,其過程叫做動態(tài)綁定(dynamic binding)。而其他大部分面向?qū)ο笳Z言,會在運行時查找“虛方法表”(virtual table)來查出執(zhí)行的方法(是調(diào)用子類的方法?還是父類的方法?)。
OC是C語言的超集,如果你熟悉C語言,那C語言里的大部分知識在編寫OC代碼時依然適用。
那么,今天的第二個Key:指針。
OC里的指針主要用來指示對象,基本語法和C語言類似。
- 例如:聲明一個字符串
NSString *str1 = @"QiShare";
語法解釋:聲明了一個名為str1的變量,其類型為NSString *。是一個指向NSString的指針。
- 錯誤案例:
NSString str2;
報錯:error:interface type cannot be statically allocated
解釋:對象不允許聲明在??臻g上
不能在棧中分配OC對象,因為OC對象所占的內(nèi)存會被分配在堆空間(heap space)上,由程序員來控制它的內(nèi)存分配。而棧空間的臨時基本數(shù)據(jù)由編譯器控制。
- 再舉一個典型案例:
xxxClass *Qi = [[xxxClass alloc] init];
xxxClass *Share = Qi;
這里有兩個分配在??臻g的xxxClass指針:Qi和Share指向了堆空間中的同一塊內(nèi)存地址。
內(nèi)存結(jié)構(gòu),圖解如下:

分配在堆中的對象,內(nèi)存必須由開發(fā)者管理。而分配在??臻g上的指針會在其棧幀彈出時自動清理。
OC將堆內(nèi)存的管理抽象成了一個機制:ARC(Automatic Reference Counting)。在OC中,不需要用malloc及free來分配或釋放對象所占的內(nèi)存。OC在運行期環(huán)境中把這部分工作抽象為一套內(nèi)存管理架構(gòu),我們稱之為“引用計數(shù)”。之后,我們會有專門的一篇文章講解ARC機制
二、為了減少編譯時間,.h文件中盡量少引入其他頭文件。
必要時可以考慮在.h文件里"向前聲明"該類。
@class QiShareClass;
@interface xxx : xxx
// ...
@end
在.m文件里再引入該類
#import "QiShareClass.h"
// ....
同時,向前聲明也解決了兩個類可能存在互相引用的問題。
例如:
- Qi.h中
#import "Share.h"
- Share.h中
#import "Qi.h"
當(dāng)解析"Qi.h"時,編譯器發(fā)現(xiàn)"Share.h",再導(dǎo)回自己本身"Qi.h"。
從而造成循環(huán)引用(chicken-and-egg situation,兩個類循環(huán)引用,這里不是指retain-circle)。這樣會導(dǎo)致兩個類中有一個類不能正確編譯。
示例如下:


推薦:如果用到協(xié)議,必要時可以把協(xié)議封裝在一個單獨的頭文件里。不僅可以減少編譯時間,還能避免循環(huán)引用的問題。
三、多用字面量語法,少用等價方法
- 好處:簡明
易讀,提高代碼的可讀性和可維護性 - 局限性:用字面量預(yù)防創(chuàng)建數(shù)組或字典時,值不能有nil,否則會拋出異常。
For Example:
// 字面量字符串
NSString *str = @"QiShare";
// 字面量數(shù)值
NSNumber *num = @(1);
NSNumber *floatNum = @(1.0);
int x = 5;
float y = 3.14;
NSNumber *num = @(x * y);
// 字面量數(shù)組
NSArray *animals = @[@"cat", @"dog", @"tiger", @"monkey"];
NSString *cat = animals[0];
// 字面量字典
NSDictionary *qiShareDic = @{@"englishName": @"QiShare",
@"chineseName": @"奇分享"}];
NSString *englishName = qiShareDic[@"englishName"];
NSString *chineseName = qiShareDic[@"chineseName"];
- 注意:用字面量語法創(chuàng)造出來的對象默認(rèn)都是不可變對象,如果需要可變對象,執(zhí)行一步 mutableCopy
NSMutableString *mutableStr = [@"QiShare" mutableCopy];
四、多用類型常量,少用#define預(yù)處理指令
- 好處:定義出來的常量包含類型信息,不可變,可讀性高。
- 而
#define定義的值只是在編譯前作字符串替換操作,并不包含類型信息。并且如果一不小心被重新定義了常量值,編譯器不會產(chǎn)生任何警告??,最終導(dǎo)致常量值不一致。
For Example:
#define ANIMATION_DURATION 0.5
// 替換成
static const NSTimeInterval kAnimationDuration = 0.5;
// 全局常量
// QiShare.h
extern const NSTimeInterval QiShareAnimationDuration;
// QiShare.m
const NSTimeInterval QiShareAnimationDuration = 0.3;
五、多用枚舉表示狀態(tài)、選項、狀態(tài)碼
- 通過枚舉表示狀態(tài)機的狀態(tài)、傳遞給方法的選項以及狀態(tài)碼等值,增強了代碼的可讀性。
- 枚舉的值如果存在多選的可能,將選項值定義為2的冪。便于底層轉(zhuǎn)成二進制存儲。
- 用
NS_ENUM與NS_OPTIONS宏來定義枚舉類型可以指明底層的數(shù)據(jù)類型。由開發(fā)者決定,而不是編譯器決定。
For Example:
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
最后,特別致謝《Effective Objective-C 2.0》第一章
關(guān)注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)