iOS 編寫高質(zhì)量Objective-C代碼(一)

級別: ★☆☆☆☆
標(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-CSwift。
今天,小編幫助大家更加熟悉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 :消息傳遞。
    OCC++、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中,不需要用mallocfree來分配或釋放對象所占的內(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_ENUMNS_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(微信公眾號)

推薦文章:iOS UIButton之防止重復(fù)點擊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容