Objective-C 基礎

關于Objective-C

Objective-C 是一種通用、高級、面向對象的編程語言。它擴展了標準的 ANSI C 編程語言寞缝,將 Smalltalk 式的消息傳遞機制加入到 ANSI C 中。目前主要支持的編譯器有 GCC 和 Clang仰泻。


歷史

Objective-C 主要由 Stepstone 公司的 Brad Cox 和 Tom Love 在 19 世紀 80 年代發(fā)明荆陆。

1981年 Brad Cox 和 Tom Love 還在 ITT 公司技術中心任職時,接觸到了 SmallTalk語言集侯。Cox 當時對軟件設計和開發(fā)問題非常感興趣被啼,他很快地意識到 SmallTalk 語言在系統(tǒng)工程構建中具有無法估量的價值。

1983 年棠枉,Cox 與 Love 合伙成立了 Productivity Products International(PPI)公司浓体,將 Objective-C 及其相關庫商品化販售,并在之后將公司改名為StepStone辈讶。

1988年命浴,斯蒂夫·喬布斯(Steve Jobs)離開蘋果公司后成立了 NeXT Computer 公司,NeXT 公司買下 Objective-C 語言的授權贱除,并擴展了 GCC 使之支持 Objective-C 的編譯生闲,基于 Objective-C 開發(fā)了 AppKit 與 Foundation Kit 等庫,作為 NeXTSTEP 的的用戶界面與開發(fā)環(huán)境的基礎月幌。

1996年12月20日碍讯,蘋果公司宣布收購 NeXT Software 公司,NEXTSTEP/OPENSTEP 環(huán)境成為蘋果操作系統(tǒng)下一個主要發(fā)行版本 OS X 的基礎扯躺。


C語言的嚴格超集

  • 任何C語言程序不經修改就可以直接通過Objective-C編譯器成功編譯
  • Objective-C 源程序中可以直接使用任何C語言代碼

正是由于以上優(yōu)勢捉兴,在 Swift 推出之后,許多和 C 有直接交互的部分大多仍舊使用 Objective-C 編寫缅帘。


SmallTalk 式的消息傳遞模型

Objective-C 最大的特色是承自 Smalltalk 的消息傳遞模型(message passing)轴术,此機制與 C Family 式的主流風格差異甚大。

如在 java 中钦无,方法調用:

obj.method(argument);

Objective-C 里的方法調用:

[obj method:argument];

Objective-C 與其說調用對象的方法逗栽,不如說向對象傳遞消息更為精確。
二者并不僅僅是語法上的差異失暂,還有基本行為上的不同彼宠。

舉個??:

[car fly];

Java 的解讀是 “調用 car 類的 fly 方法”。
若 car 類里沒有定義 fly 方法弟塞,那編譯不會通過凭峡。

Objective-C 里,則解讀為 “向 car 對象發(fā)送 fly 消息”决记。
若 car 類內定義有 fly 方法就運行方法內的代碼摧冀,若 car 內不存在 fly 方法,則程序依舊可以通過編譯,運行期則拋出異常: unrecognized selector sent to instance 0x8871710

Objective-C 因為運行期才處理消息索昂,允許發(fā)送未知消息給對象建车。同時空對象 nil 接受消息后默認為不做事,所以送消息給 nil 也不用擔心程序崩潰椒惨。


布爾(BOOL)

Java 的布爾數(shù)據類型 boolean 具有 true 和 false 兩個值缤至,Objective-C 的 BOOL 具有 YES 和 NO 兩個值。

Objective-C 中的 BOOL 實際上一種對帶符號的字符類型(signed char)的類型定義康谆。通過 #define 把 YES 定義為1领斥,NO 定義為0。


字符串(NSString)

Objective-C 字符串由雙引號包裹沃暗,并在引號前加一個@符號月洛,如:

NSString *name = @"Tony";


import 語句

在C語言中,使用 #include 導入入頭文件孽锥。在Objective-C中膊存,類似的指令 #import 保證一個文件只會被包含一次,類似于一般頭文件中的:

#ifndef XXX
#define XXX ...
#endif


類的定義與實現(xiàn)

Objective-C 中將類的定義(interface)與實現(xiàn)(implementation)分為兩個部分忱叭。
類的定義文件遵循 C 語言慣例以 .h 為后綴,實現(xiàn)文件以 .m 為后綴今艺。

定義部分韵丑,定義類的名稱、數(shù)據成員和方法虚缎。 以關鍵字 @interface 開始撵彻,@end 結束:

@interface MyClass : NSObject {
    int memberVar1; // 實體變量
    id  memberVar2;
}

+ (return_type)class_method; // 類方法

- (return_type)instance_method1; // 實例方法
- (return_type)instance_method2:(int)p1;
- (return_type)instance_method3:(int)p1 andPar:(int)p2;
@end

實現(xiàn)部分,以關鍵字 @implementation 開始实牡,@end 結束:

@implementation MyClass {
    int memberVar3; // 私有實體變量
}

- (return_type)instance_method1 {
    ....
}

- (return_type)instance_method2:(int)p1 {
    ....
}

- (return_type)instance_method3:(int)p1 andPar:(int)p2 {
    ....
}
@end

上述代碼 Java 版對照:

public class MyClass {
    protected int memberVar1;
    protected pointer memberVar2;
    private int memberVar3;
    
    public (return_type) instance_method1() {
        ....
    }
    
    public (return_type) instance_method2(int p1) {
        ....
    }
    
    public (return_type) instance_method3andPar(int p1, int p2) {
        ....
    }
}

方法前面的 +/- 代表函數(shù)的類型:加號(+)代表類方法(class method)陌僵,不需要實例就可以調用,與 Java 的靜態(tài)方法相似创坞。減號(-)即是一般的實例方法(instance method)碗短。


創(chuàng)建對象

Objective-C 創(chuàng)建對象需通過 alloc 和 init 兩個消息。alloc 是分配內存题涨,init 則是初始化對象偎谁。 init 與 alloc 都是定義在 NSObject 里的方法,父對象收到這兩個信息并做出正確回應后纲堵,新對象才創(chuàng)建完畢巡雨。

舉個??:

MyObject *obj = [[MyObject alloc] init];

MyObject *obj = [MyObject new]; // 也可以這么寫,new 相當于 alloc + init

Java 版本:

MyObject obj = new MyObject();


協(xié)議(Protocol)

協(xié)議類似與 Java 語言中的接口席函。

協(xié)議的定義以 @protocol 開頭铐望,@end 結尾:

@protocol SayHello
- (void)sayHello;
@end

協(xié)議中定義的方法分為必須實現(xiàn)的方法和可選實現(xiàn)的方法。協(xié)議中的方法默認必須實現(xiàn),可選實現(xiàn)的方法以 @optional 為標識正蛙。

@protocol SomeProtocol
 - (void)method1; // 必須實現(xiàn)
@optional
- (void)method2; // 可選實現(xiàn)
- (void)method3; // 可選實現(xiàn)
@end

實現(xiàn)一個協(xié)議:

@interface SomeClass : SomeSuperClass <SomeProtocol>
@end
@implementation SomeClass
 - (void)method1 {
  // ...
}

- (void)method2 {
  // ...
}

- (void)method3 {
  // ...
}
@end


分類(Category)

分類可以給一個已經存在的類增加方法督弓,而不用去改它的源碼。類似于 Swift 和 Kotlin 中的擴展(extension)跟畅。

比如咽筋,NSString 是 Objective-C 內置的系統(tǒng)類,我們創(chuàng)建一個它的分類以支持加法運算:

// interface 部分
@interface NSString (Calculation)
- (NSString *)stringByAdding:(NSString *)aString; // 加
@end

// implementation 部分
@implementation NSObject (Calculation)
- (NSString *)stringByAdding:(NSString *)aString {
    // ...
}
@end

使用的時候徊件,只要包含NSObject+Calculation.h奸攻,就可以使用了:

NSString *str = @"100";
NSString *result = [str stringByAdding:10]; // result is 110


引用計數(shù)

Objective-C 使用引用計數(shù)來管理對象的生命周期。

如果想使某個對象繼續(xù)存活虱痕,那就遞增其引用計數(shù)(reatain)睹耐;用完了之后,就遞減其計數(shù)(release)部翘。當計數(shù)為0時硝训,系統(tǒng)就會將它銷毀。

對象操作 Objective-C方法 操作結果
生成并持有對象 alloc, new, copy, mutableCopy 等方法 生成對象并設置引用計數(shù) =1
持有對象 reatain 方法 引用計數(shù) +1
釋放對象 release 方法 引用計數(shù) -1
廢棄對象 dealloc 方法 系統(tǒng)自動調用 引用計數(shù) =0 時調用



memory_management_2x.png



iOS5 開始引入了自動引用計數(shù) (ARC新思, Automatic Reference Counting)
簡單地說窖梁,ARC 在編譯時為代碼在合適的位置加上 retain 和 release。


屬性

屬性(property)是 Objective-C 的一項特性夹囚,用于封裝對象中的數(shù)據纵刘。實例變量一般通過“存取方法”(access method)來訪問。其中荸哟,“獲取方法”(getter)用于讀取變量值假哎,而“設置方法”(setter)用于寫入變量值。

Xcode 4.4時代之前鞍历,需要一個屬性的時候:

// .h
@property NSObejct *foo;

// .m
@synthesize foo = _foo;

頭文件中加上@property舵抹,那么編譯器會自動添加下面一段代碼:

- (NSObject *)foo; 
- (void)setFoo:(NSObject *)newFoo; 

在 .m 中實現(xiàn):

- (NSObject *)foo {
    return _foo;
}

- (void)setFoo:(NSObject *)newFoo {
    [foo retain];
    [_foo release];
    _foo = foo;
}

自動引用計數(shù)時代,喜大普奔劣砍,一切都變得簡單了惧蛹,只需要一句話:

@property (nonatomic, strong) NSObejct *foo;



@property(*) 括號中的屬性內容:

  • 原子性:atomic 和 nonatomic,默認為atomic秆剪。
  • 讀寫權限:readwrite 和 readonly赊淑, 默認為 readwrite,同時生成setter和getter仅讽,所以本質上是由@synthesize來實現(xiàn)的陶缺。而 readonly 就是代表只生成 getter 方法。
  • 指定方法名:使用getter=<name>或setter=<name>來指定方法名洁灵。
  • 內存管理:assign饱岸,strong掺出,weak,copy苫费。



當 B 是 A 的 strong 屬性汤锨,A 又是 B 的 strong 屬性,此時就會造成循環(huán)引用百框,如圖:

QQ20180730-212815@2x.png

此時闲礼,A 若想被釋放,需要引用計數(shù)為0铐维。而 B 持有 A柬泽,所以想要 A dealloc,需要 B 發(fā)送 release 消息到 A嫁蛇。而 B 只有 dealloc 時锨并,才會發(fā)送 release 到 A,并且 B dealloc 也需要 A 向其發(fā)送 release 消息睬棚。這樣 A 和 B 互相等待對方的 release 消息第煮,造成循環(huán)引用,導致內存無法釋放抑党。

解決循環(huán)引用:將其中一方的屬性使用 weak 修飾包警。


點語法

屬性合成的方法可以通過點語法調用:

obj.foo = newFoo;

// 上述代碼等同于
[obj setFoo:newFoo];

點語法的本質還是方法調用,是一種編譯器行為底靠,編譯器會自動進行轉換揽趾,來判斷調用 set 方法還是 get 方法。

操作 點語法 使用消息表達式
setter obj.name = val; [obj setName:val];
getter val = obj.name; val = [obj name];


代碼塊 (block)

block 對象是對函數(shù)的擴展苛骨。除了函數(shù)中的代碼,block 還包含變量綁定苟呐。block 也被稱為閉包(closure)痒芝。block 是以 “^” 開頭為標識的。后面跟的一個括號標示 block 需要的參數(shù)列表牵素。

一個簡單的??:

void (^myBlock) (int) = ^(int input) {
    NSLog(@"input number is %d", input);
};

// 調用 block 
myBlock(1);
// 輸出:input number is 1

block 使用局部變量:

NSString *name = @"Candy";
void (^myBlock) (void) = ^ {
    NSLog(@"name is %@", name);
};

myBlock();
// 輸出:name is Candy

block 可以方便的使用局部變量 name严衬,但是你不能修改它,當你嘗試修改 name 時笆呆,會報錯 Variable is not assignable (missing __block type specifier)请琳。

此時你需要用 __block 修飾 name:

__block NSString *name = @"Candy";
void (^myBlock) (void) = ^ {
    name = @"Jack"
    NSLog(@"name is %@", name);
};

myBlock();
// 輸出:name is Jack

block 使用實例變量:

// 定義的實例變量 userName
@property (nonatomic, copy) NSString *name;

// block 使用實例變量
void (^myBlock) (void) = ^ {
    NSLog(@"ins is %@", self.name);
};

當 block 本身為實例變量,而 block 內部又使用了 實例變量赠幕,此時就會出現(xiàn)循環(huán)引用俄精。
舉個??:

// 定義的實例變量 userName 和 myBlock
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void (^myBlock)(void);

// block 使用實例變量
_myBlock = ^ {
    NSLog(@"name is %@", self.name);
 };

此時會有警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
簡單來說,是因為 self 引用了 myBlock榕堰,myBlock 又引用了 self竖慧。

解決 block 循環(huán)引用:

// 定義的實例變量 userName 和 myBlock
@property (nonatomic, copy) NSString *userName;
@property (nonatomic, copy) void (^myBlock)(void);

// block 使用實例變量
__weak typeof(self) weakSelf = self;
self.myBlock = ^ {
    NSLog(@"name is %@", weakSelf.userName);
 };




最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末嫌套,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子膏潮,更是在濱河造成了極大的恐慌痒谴,老刑警劉巖碾篡,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異痹筛,居然都是意外死亡,警方通過查閱死者的電腦和手機廓鞠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門帚稠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诫惭,你說我怎么就攤上這事翁锡。” “怎么了夕土?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵馆衔,是天一觀的道長。 經常有香客問我怨绣,道長角溃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任篮撑,我火速辦了婚禮减细,結果婚禮上,老公的妹妹穿的比我還像新娘赢笨。我一直安慰自己未蝌,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布茧妒。 她就那樣靜靜地躺著萧吠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桐筏。 梳的紋絲不亂的頭發(fā)上纸型,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音梅忌,去河邊找鬼狰腌。 笑死,一個胖子當著我的面吹牛牧氮,可吹牛的內容都是我干的琼腔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼踱葛,長吁一口氣:“原來是場噩夢啊……” “哼展姐!你這毒婦竟也來了躁垛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤圾笨,失蹤者是張志新(化名)和其女友劉穎教馆,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擂达,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡土铺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了板鬓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悲敷。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俭令,靈堂內的尸體忽然破棺而出后德,到底是詐尸還是另有隱情,我是刑警寧澤抄腔,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布瓢湃,位于F島的核電站,受9級特大地震影響赫蛇,放射性物質發(fā)生泄漏绵患。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一悟耘、第九天 我趴在偏房一處隱蔽的房頂上張望落蝙。 院中可真熱鬧,春花似錦暂幼、人聲如沸筏勒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奏寨。三九已至,卻和暖如春鹰服,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揽咕。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工悲酷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亲善。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓设易,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛹头。 傳聞我的和親對象是個殘疾皇子顿肺,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容