1. Objective-C簡介
Objective-C語言簡介
- Objective-C在C語言基礎(chǔ)上做了面向?qū)ο髷U(kuò)展呈野。
- 1983年由Brad Cox和Tom Love發(fā)明,后成為NeXT的主力語言印叁,后被蘋果收購被冒,成為蘋果開發(fā)平臺的主力語言。
- 與Cocoa和Cocoa Touch框架高度集成轮蜕,支持開發(fā)Mac OS X昨悼、iOS應(yīng)用。
- 在蘋果開發(fā)平臺上跃洛,通過LLVM編譯器架構(gòu)率触,支持與Swift語言雙向互操作。
在iOS開發(fā)平臺上支持的語言有Swift汇竭、Objective-C葱蝗、C/C++,主要使用前兩者细燎。
?
如何掌握高級編程語言
底層思維:向下两曼,如何把握機(jī)器底層從微觀理解對象構(gòu)造
- 語言轉(zhuǎn)換
- 編譯轉(zhuǎn)換
- 內(nèi)存模型
- 運(yùn)行時(shí)機(jī)制
抽象思維:向上,如何將我們的周圍世界抽象為程序代碼
- 面向?qū)ο?/li>
- 組件封裝
- 設(shè)計(jì)模式
- 架構(gòu)模式
「時(shí)空人」三位一體分析法
- 對時(shí)間分析——發(fā)生在什么時(shí)候玻驻?
- Compile-time VS Run-time
- 對空間分析——變量放在哪里悼凑?
- Stack VS Heap
- 人物分析——代碼哪里來的?
- Programmer VS Compiler/Runtime/Framework
兩種開發(fā)方式
- Clang或GCC命令行
clang -fobjc-arc HelloWorld.m -o HelloWorld
- 或
gcc -fobjc-arc HelloWorld.m -o HelloWorld
击狮,推薦用clang-fobjc-arc
:支持ARC(Automatic Reference Counting)- 適合調(diào)試佛析、研究、微觀探查
-
-o
:輸出文件名 - 更多可以參考
-help
//引入頭文件彪蓬,可以避免多次引入重復(fù)頭文件寸莫,推薦使用。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//類似于prinft,但多了日期時(shí)間等信息谒获。
NSLog(@"Hello World~");
}
return 0;
}
- Objective-C的字符串前要加
@
- Xcode項(xiàng)目
- 構(gòu)建正規(guī)工程項(xiàng)目
- 使用大型框架宋彼,追求設(shè)計(jì)質(zhì)量與代碼組織
Objective-C編譯過程
- GCC: GCC Front End -> GCC Optimizer -> GCC Code Generator
- LLVM-GCC: GCC Front End -> LLVM Optimizer -> LLVM Code Generator
- LLVM-Clang: Clang Front End -> LLVM Optimizer -> LLVM Code Generator
- Objective-C直接生成機(jī)器碼
- LLVM:Low Level Virtual Machine
- LLVM-Clang:目前iOS上一般使用的方式,針對iOS優(yōu)化更好披坏。
學(xué)習(xí)資源
課程總結(jié)
- 了解Objective-C語言和編譯架構(gòu)
- 了解Objective-C開發(fā)工具
- 了解機(jī)器思維和抽象思維
- 駕馭工具,而不是被工具駕馭
- 掌握英文學(xué)習(xí)資源很重要
2. 類與對象
類型系統(tǒng)
- 引用類型 Reference Type
- 類 Class
- 指針 Pointer
- 塊 Block
- 值類型 Value Type
- 基礎(chǔ)數(shù)值類型
- 結(jié)構(gòu) Struct
- 枚舉 Enum
- 類型裝飾
- 協(xié)議 Protocol
- 類別 Category
- 擴(kuò)展 Extension
- C語言中的數(shù)據(jù)類型均可用在Objective-C中
類 VS 結(jié)構(gòu)
- 類型與實(shí)例
- 類與對象
- 結(jié)構(gòu)與值
- 類——引用類型
- 位于棧上的指針(引用)
- 位于堆上的實(shí)體對象
- 結(jié)構(gòu)——值類型
- 實(shí)例直接位于棧中
- 空間分析
- 運(yùn)行時(shí)內(nèi)存圖——「胸中有溝壑」
類定義
RPoint.h
@interface RPoint: NSObject
@property int x; //屬性盐数,狀態(tài)棒拂,這里默認(rèn)初始化為0
@property int y;
- (void) print; //方法,行為
@end
-
- (void)print
:-
表示實(shí)例方法,+
表示類方法帚屉,其后的括號內(nèi)為返回值類型谜诫。
RPoint.m
#import <Foundation/Foundation.h>
#import "RPoint.h"
@implementation RPoint
- (void) print {
NSLog(@"[%d, %d]", self.x, self.y);
}
@end
SPoint.h
typedef struct {
int x;
int y;
}SPoint;
對象的空間分析
棧上存儲指針(引用),堆上存儲真正的對象攻旦,
值的空間分析
實(shí)例(值)內(nèi)存直接存儲在椨骺酰空間
棧 VS 堆
棧:存儲值類型
- 無ARC負(fù)擔(dān),由系統(tǒng)自動管理牢屋,以執(zhí)行函數(shù)為單位
- 空間大小編譯時(shí)確定(參數(shù)+局部變量)
- 函數(shù)執(zhí)行時(shí)且预,系統(tǒng)自動分配一個(gè)Stack
- 函數(shù)執(zhí)行結(jié)束,系統(tǒng)立即自動回收Stack
- 函數(shù)之間通過拷貝值傳遞
- 具有局部性烙无,大小有限額锋谐,超出會Stack Overflow
堆:存儲引用類型對象
- 分配由程序員手動請求(創(chuàng)建對象時(shí))
- 釋放由運(yùn)行時(shí)ARC機(jī)制自動釋放(確定時(shí))
- 函數(shù)之間通過拷貝引用(指針)傳遞
- 具有全局性,總體無大小限制(受制于系統(tǒng)內(nèi)存整體大兄迓)
main.m
#import <Foundation/Foundation.h>
#import "RPoint.h"
#import "SPoint.h"
void process(RPoint* rp3, SPoint sp3);
int main(int argc, const char * argv[]) {
@autoreleasepool {
//Objective-C中所有的對象均以指針的形式存在怀估,要加 * 號
//中括號為發(fā)送消息(方法調(diào)用)
//創(chuàng)建對象,alloc為向系統(tǒng)請求內(nèi)存分配
RPoint* rp1 = [[RPoint alloc] init];
rp1.x = 10;
rp1.y = 20;
[rp1 print]; //[10, 20]
//
SPoint sp1;
sp1.x = 10;
sp2.y = 20;
NSLog(@"------拷貝------")
RPoint rp2 = rp1;
rp2.x++;
rp2.y++;
//引用傳遞
[rp1 print]; //[11, 21]
[rp2 print]; //[11, 21]
SPoint sp2 = sp1;
sp2.x++;
sp2.y++;
//值傳遞
NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
NSLog(@"[%d, %d]", sp2.x, sp2.y); //[11, 21]
NSLog(@"------傳參------");
process(rp1, sp1);
[rp1 print]; //[12, 22]
NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
}
return 0;
}
//函數(shù)開始執(zhí)行時(shí)自動創(chuàng)建一個(gè)新棧合搅,與main函數(shù)的棧不同多搀,結(jié)束時(shí)會自動銷毀
void process(RPoint* rp3, SPoint sp3) {
rp3.x++;
rp3.y++;
sp3.x++;
sp3.y++;
[rp3 print]; //[12, 22]
NSLog(@"[%d, %d]", sp3.x, sp3.y); //[11, 21]
//函數(shù)執(zhí)行結(jié)束后rp3和sp3將被銷毀
//但rp3僅是指針,銷毀后指針?biāo)赶虻膶ο蟛⒉皇苡绊憽?}
?
3. 數(shù)據(jù)成員:屬性與實(shí)例變量
類型成員 Type Member
數(shù)據(jù)成員 Data Member 描述對象狀態(tài)
- 實(shí)例變量 Instance Variable
- 屬性 Property
函數(shù)成員 Function Member 描述對象行為
- 方法 Method
- 初始化器 Init
- 析構(gòu)器 Dealloc
認(rèn)識屬性
屬性表達(dá)實(shí)例狀態(tài)灾部,描述類型對外接口康铭。相比直接訪問實(shí)例變量,屬性可以做更多控制赌髓。
默認(rèn)情況下从藤,編譯器會為屬性定義propertyName自動合成:
- 一個(gè)getter訪問器方法:propertyName
- 一個(gè)setter訪問器方法:setPropertyName
- 一個(gè)實(shí)例變量:_propertyName
可自定義訪問器方法,也可更改訪問器方法名锁蠕、或?qū)嵗兞棵?/p>
可以使用靜態(tài)全局變量(C語言)+ 類方法夷野,模擬類型屬性。
屬性聲明:
@property NSString* firstName;
//--上述代碼將生成:--
NSString* _firstName;
- (NSString*) firstName {/**code**/}
- (void) setFirstName: (NSString*)newValue {/**code**/}
//----
//更改訪問器方法名
@property (getter = GivenName, setter = setGivenName:) NSString* lastName;
//更改實(shí)例變量名
@synthesize firstName = givenName;
調(diào)用方法:
//訪問器方法
[employee setFirstName: @"Tom"];//set
[employee firstName]; //get
//屬性表達(dá)式荣倾,推薦悯搔。
employee.firstName = @"Tom"; //set
NSLog(@"First Name: %@", employee.firstName);//get
模擬靜態(tài)(類)屬性
在.m文件中定義一個(gè)靜態(tài)變量
//靜態(tài)變量
static int _max = 100;
@implementation Employee {
//...
}
在.h文件中定義方法
@interface Employee: NSObject
//...
+ (int) max;
+ (void) setMax: (int)newValue;
//...
@end
在.m文件中實(shí)現(xiàn)方法
@implementation Employee {
//...
//為靜態(tài)變量提供訪問器方法
+ (int) max {
return _max;
}
+ (void) setMax: (int)newValue {
_max = newValue;
}
//...
}
調(diào)用方法:
//Employee為類型,不是對象
[Employee setMax: 300];
Employee.max = 400;
NSLog(@"class variable is %d.", Employee.max);
實(shí)例變量
可以定義實(shí)例變量舌仍,而不定義屬性妒貌。只有實(shí)例變量,沒有類變量铸豁。
如果同時(shí)自定義了getter和setter訪問器方法灌曙,或者針對只讀屬性定義了getter訪問其方法,編譯器將不再合成實(shí)例變量节芥。
在類外一律使用屬性來訪問在刺,類內(nèi)大多也通過self使用屬性訪問。只有一下情況使用實(shí)例變量來訪問:
- 初始化器 init
- 析構(gòu)器 dealloc
- 自定義訪問器方法
- 實(shí)例變量只能在類內(nèi)訪問,類外不可訪問蚣驼。
- 引用類型的屬性使用實(shí)例變量訪問時(shí)可能會?有內(nèi)存管理的問題忍燥。
實(shí)例變量的生存周期
實(shí)例變量的存儲:跟隨對象實(shí)例存儲在堆上。
值類型實(shí)例變量直「內(nèi)嵌」在對象實(shí)例中隙姿。跟隨對象實(shí)例內(nèi)存釋放而被釋放。
引用類型實(shí)例變量通過指針「引用」堆上的引用類型實(shí)例厂捞,ARC針對引用進(jìn)行計(jì)數(shù)管理输玷,自動釋放引用計(jì)數(shù)為0的對象。
屬性的描述特性
屬性描述特性(Attribute)可以指定屬性不同環(huán)境下的不同功能靡馁。
- 讀寫特性
- 讀寫屬性 readwrite (默認(rèn))
- 只讀屬性 readonly
- 多線程特性
- 原子性 atomic (默認(rèn))
- 非原子性 nonatomic
- 內(nèi)存管理特性
- ARC環(huán)境
- 強(qiáng)引用 strong (默認(rèn))
- 弱引用 weak 阻止循環(huán)引用
- 拷貝屬性 copy 為屬性賦值時(shí)創(chuàng)建獨(dú)立拷貝
- 其他情況
- retain
- assign
- unsafe_unretained
@property (readonly, nonatomic) NSString* firstName;
- 避免循環(huán)引用:使用weak屬性
4. 函數(shù)成員:方法
函數(shù)成員 Function Member 描述對象行為
- 方法 Method
- 初始化器 Init
- 析構(gòu)器 Dealloc
認(rèn)識方法 Method
函數(shù):代碼段上的可執(zhí)行指令序列
- 全局函數(shù)(C語言函數(shù))
- 成員函數(shù)(Objective-C方法)
方法是類的成員函數(shù)欲鹏,表達(dá)實(shí)例行為或類型行為。
所有方法默認(rèn)為公有方法臭墨。沒有private或protected方法赔嚎。
動態(tài)消息分發(fā):方法調(diào)用通過運(yùn)行時(shí)動態(tài)消息分發(fā)實(shí)現(xiàn),在對象上調(diào)用方法又稱「向?qū)ο蟀l(fā)送消息」胧弛。
實(shí)例方法或類型方法
實(shí)例方法——表達(dá)實(shí)例行為尤误,可以訪問
- 實(shí)例成員(實(shí)例屬性、實(shí)例變量结缚、實(shí)例方法)
- 類型方法损晤、靜態(tài)變量
類方法——表達(dá)類型行為,訪問權(quán)限:
- 可以訪問:類型方法红竭、靜態(tài)變量
- 不能訪問:實(shí)例成員(實(shí)力屬性尤勋、實(shí)例變量、實(shí)例方法)
了解編譯器背后對實(shí)例方法和類方法的不同處理:self指針
//實(shí)例方法
- (void) print {
NSLog(@"[%d, %d]", self.x, self.y);
}
//編譯器編譯后:
void print(BLNPoint* self) {
NSLog(@"[%d, %d]", self.x, self.y);
}
//調(diào)用時(shí):
[p1 print];
//編譯器編譯后:
print(p1);
//類型方法
+ (BLNPoint*) getOriginPoint {
//...
//在類型方法中茵宪,self 等同于類型最冰,與實(shí)例方法中的self不同。
[self process];
//等同于
[BLNPoint process];
[self print];//錯(cuò)誤
}
//編譯器編譯后:
BLNPoint* getOriginPoint() {
//...
}
//調(diào)用時(shí):
BLNPoint* origin = [BLNPoint getOriginPoint];
//編譯器編譯后:
BLNPoint* origin = getOriginPoint();
方法參數(shù)
- 如果參數(shù)類型為值類型稀火,則為傳值方式暖哨;如果參數(shù)類型為引用類型,則為傳指針方式憾股。
- 方法可以沒有參數(shù)鹿蜀,也可以沒有返回值。
- 如果方法有參數(shù)服球,方法名約定包含第一個(gè)參數(shù)名茴恰,第二個(gè)參數(shù)開始需要顯示提供外部參數(shù)名。
- 調(diào)用時(shí)斩熊,第一個(gè)參數(shù)名忽略往枣,但后面的參數(shù)名必須顯式標(biāo)明。
- (BOOL) isEqualToPoint: (BLNPoint*) point;
- (void) moveToX: (int)x toY: (int)y;
//調(diào)用
[p1 isEqualToPoint: p2];
[p1 moveToX: 100 toY: 200];
-
id
可以表示所有的類型
id obj = [[BLNPoint alloc] init];
[obj moveToX: 50 toY: 60];
[obj print];
[obj setX: 70];
obj.x = 70;//obj聲明為id時(shí)不可以這樣用
- 理解動態(tài)方法調(diào)用機(jī)制——消息分發(fā)表
5. 初始化器與析構(gòu)器
認(rèn)識初始化器與析構(gòu)器
初始化器用于初始化對象實(shí)例或者類型,是一個(gè)特殊的函數(shù)分冈。
- 對象初始化器:- (id) init 可以重載多個(gè)
- 類型初始化器:+ (void) initialize 只能有一個(gè)
析構(gòu)器用于釋放對象擁有的資源圾另,無返回值的函數(shù)。
- 對象析構(gòu)器 - (void) dealloc 只能有一個(gè)
- 沒有類型析構(gòu)器
- (id) init;
- (id) initWithName: (NSString *)name;
- (id) initWithName: (NSString *)name WithPages: (int)pages;
- (void) dealloc;
+ (void) initialize;
對象初始化器
初始化對象實(shí)例時(shí)雕沉,init通常和alloc搭配使用集乔。
alloc所做的事情——NSObject已實(shí)現(xiàn):
- 在堆上分配合適大小的內(nèi)存。
- 將屬性或者實(shí)例變量的內(nèi)存置0坡椒。
init所做的事情——可以自定義:
- 調(diào)用父類初始化器 [super init] (前置調(diào)用)扰路。
- 初始化當(dāng)前對象實(shí)例變量(注意使用實(shí)例變量,不要使用屬性)倔叼。
new 相當(dāng)于調(diào)用alloc/init的無參數(shù)版本汗唱。
- (id) init {
self = [super init];
if (self) {
//...
}
return self;
}
- (id) initWithName: (NSString *)name WithPages: (int)pages {
self = [super init];
if (self) {
//初始化實(shí)例對象,使用實(shí)例變量丈攒,不要使用屬性self.name
_name = [name copy];
_pages = pages;
}
return self;
}
//調(diào)用:
Book* book = [Book new];
//相當(dāng)于
Book* book = [[Book alloc] init];
類初始化器
類初始化器initialize負(fù)責(zé)類型級別的初始化哩罪。
initialize在每個(gè)類使用之前被系統(tǒng)自動調(diào)用,且每個(gè)進(jìn)程周期中巡验,只被調(diào)用一次际插。
子類的initialize會自動調(diào)用父類的initialize(前置調(diào)用)。
+ (void) initialize {
if (self == [Book class]) {
//...
}
}
對象析構(gòu)器
對象析構(gòu)器dealloc負(fù)責(zé)釋放對象擁有的動態(tài)資源:
- 自動實(shí)現(xiàn):1. ARC將對象屬性引用計(jì)數(shù)減持
- 手動實(shí)現(xiàn):2. 釋放不受ARC管理的動態(tài)內(nèi)存腹鹉,如malloc分配的內(nèi)存
- 手動實(shí)現(xiàn):3. 關(guān)閉非內(nèi)存資源,如文件句柄敷硅、網(wǎng)絡(luò)端口...
dealloc由ARC根據(jù)對象引用計(jì)數(shù)規(guī)則功咒,在釋放對象內(nèi)存前自動調(diào)用力奋,無法手工調(diào)用。
子類的dealloc會自動調(diào)用父類的dealloc(后置調(diào)用)猿挚。
6. 繼承與多態(tài)
認(rèn)識面向?qū)ο?/h3>
封裝 Encapsulation
隱藏對象內(nèi)部實(shí)現(xiàn)細(xì)節(jié),對外僅提供公共接口訪問伊约。
繼承 Inheritance
一個(gè)類型在另外類型基礎(chǔ)上進(jìn)行的擴(kuò)展實(shí)現(xiàn)屡律。
多態(tài) Polymorphism
不同類型針對同一行為接口的不同實(shí)現(xiàn)方式超埋。
認(rèn)識繼承 Inheritance
繼承:每一個(gè)類只能有一個(gè)基類,子類自動繼承基類的:
- 實(shí)例變量
- 屬性
- 實(shí)例方法
- 類方法
了解所有類的根類:NSObject
繼承的兩層含義:
- 成員復(fù)用:子類復(fù)用基類成員
- 類型抽象:將子類當(dāng)做父類來使用(IS-A關(guān)系準(zhǔn)則Circle is a Shape)
Objective-C只支持單繼承,一個(gè)類只能有一個(gè)基類
如果希望實(shí)例變量在類外可以訪問絮蒿,可以放在接口文件中但壮,必須放在大括號內(nèi)并加上@public胳施,一般情況下不推薦
@interface Shape: NSObject {
@public int _data;
}
//調(diào)用時(shí)
shape->_data++;
認(rèn)識運(yùn)行時(shí)多態(tài) Polymorphism
多態(tài):子類在父類統(tǒng)一行為接口下焦辅,表現(xiàn)不同的實(shí)現(xiàn)方式。
對比重寫與重載
- 子類重寫父類同名同參數(shù)方法:子類只可以重寫override父類方法
- 方法名相同跟继、參數(shù)不同:Objective-C不支持方法的重載舔糖。
在子類的代碼中摇庙,可以使用super來調(diào)用基類的實(shí)現(xiàn)单匣。
- self具有多態(tài)性户秤,可以指向不同子類
- super,沒有多態(tài)性,僅指向當(dāng)前父類
- 屬性的setter和getter方法也可以被重寫
理解self的多態(tài)性:
//Shape類
- (void) draw {
NSLog(@"Shape object draw");
}
- (void) move {
NSLog(@"Shape object move");
//這種情況下:self指代rectangle
//所以會調(diào)用rectangle的draw方法
[self draw];
}
//------------------------------
//Rectangle類繼承自Shape
- (void) draw {
NSLog(@"Rectangle object draw");
}
/**
//可以理解為Rectangle有一個(gè)隱藏的move方法
//和Shape類中的一模一樣
- (void) move {
NSLog(@"Shape object move");
//這種情況下:self指代rectangle
//所以會調(diào)用rectangle的draw方法
[self draw];
}
**/
//----------------------------
//調(diào)用時(shí)
//Shape是聲明類型模捂,Rectangle是實(shí)際類型
//所以這兩行的執(zhí)行結(jié)果是相同的
Shape* rectangle = [[Rectangle alloc] init];
//Rectangle* rectangle = [[Rectangle alloc] init];
[rectangle draw];//Rectangle object draw
//這里會輸出:
//Shape object move
//Rectangle object draw
[rectangle move];
繼承中的init和dealloc
初始化器 init
- 子類自動繼承基類的初始化器
- 子類也可以重寫基類初始化器岖食,此時(shí)子類初始化器必須首先調(diào)用基類的一個(gè)初始化器(手工調(diào)用)忠寻。
析構(gòu)器 dealloc
- 子類可以選擇繼承基類析構(gòu)器,或者重寫基類析構(gòu)器捐腿。
- 子類析構(gòu)器執(zhí)行完畢后纵朋,會自動調(diào)用基類析構(gòu)器(后置調(diào)用,且在開啟ARC后不支持手工調(diào)用)
- 子類析構(gòu)器自動具有多態(tài)性
Tips: 盡量避免在父類init和dealloc中調(diào)用子類重寫的方法茄袖。
- (id) init {
self = [super init];
if (self) {
//...
}
return self;
}