前言
如何掌握高級編程語言
- 底層思維: 向下,如何把握機(jī)器底層從微觀理解對象構(gòu)造
- 語言構(gòu)造
- 編譯轉(zhuǎn)換
- 內(nèi)存模型
- 運(yùn)行時機(jī)制
- 抽象思維:向上宣渗,如何將我們的周圍世界為程序代碼
- 面向?qū)ο?/li>
- 組件封裝
- 設(shè)計模式
- 架構(gòu)模式
兩種開發(fā)方式
- Clang或GCC命令行
clang-fobjc-arc helloword.m
-
-fobjc-arc
支持ARC內(nèi)存管理 - 適合調(diào)試述召、研究跃赚、微觀探查
- Xcode項(xiàng)目
- 構(gòu)建正規(guī)工程項(xiàng)目
- 使用大型框架妓柜,追求設(shè)計質(zhì)量與代碼組織
- 目前oc采用的LLVM-Clang的編譯架構(gòu)
類與對象
使用#import和#include的區(qū)別
- import是oc語音的頭文件導(dǎo)入,它能避免重復(fù)導(dǎo)入裸删。確保頭文件只會被導(dǎo)入一次。
- include如果不注意很容易會重復(fù)導(dǎo)入阵赠,出現(xiàn)相互包含的編譯錯誤涯塔。推薦使用import來導(dǎo)入頭文件。
類和對象
- 類是對某一事物的描述清蚀,是抽象的匕荸,而對象是一個實(shí)實(shí)在在的個體,是類的一個實(shí)例枷邪。
- 類是對象的抽象榛搔,而對象是類的具體實(shí)例。
類型系統(tǒng)(不同的類型)
- 引用類型(復(fù)制的指針)
- 類 class
- 指針 pointer
- 塊 block
- 值類型(直接復(fù)制的數(shù)值)
- 基礎(chǔ)數(shù)據(jù)類型东揣,int float等
- 結(jié)構(gòu) struct
- 枚舉 enum
- 類型裝飾
- 協(xié)議 protocol
- 類別 category
- 擴(kuò)展 extension
類class和結(jié)構(gòu)體struct的區(qū)別
- 從類型和實(shí)例關(guān)系來看
- class是類(類型)和對象(實(shí)例)
- struct是結(jié)構(gòu)(類型)和值(實(shí)例)
- class是引用類型践惑,位于棧上的是指針(引用),位于堆上的是實(shí)體對象
- struct是值類型嘶卧,實(shí)例(值)內(nèi)存直接位于棧中
棧與堆的區(qū)別
內(nèi)存管理的范圍
- 任何繼承了NSObject的對象
- 對其他非對象類型無效
- 即只有oc對象需要進(jìn)行內(nèi)存管理尔觉,非oc對象類型比如基本的數(shù)據(jù)類型不需要進(jìn)行內(nèi)存管理
引入棧和堆的概念
棧(stack)
- 棧區(qū)(stack)是由編譯器自動分配并釋放,存放函數(shù)的參數(shù)值芥吟,局部變量等侦铜。棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),對應(yīng)線程/進(jìn)程是唯一的运沦。優(yōu)點(diǎn)是快速高效泵额,缺點(diǎn)是有限制,數(shù)據(jù)不靈活(先進(jìn)后出)携添。
- 椉廾ぃ空間有兩種分配方式:
- 靜態(tài)分配是由編譯器完成,比如局部變量的分配
- 動態(tài)分配由
alloca
函數(shù)完成烈掠,動態(tài)分配是無須釋放的(自動釋放)
堆(heap)
- 堆(heap)是由程序員分配和釋放的羞秤,如果不釋放,程序結(jié)束時左敌,可能會由操作系統(tǒng)回收 瘾蛋,比如在ios 中 alloc 都是存放在堆中。優(yōu)點(diǎn)是靈活方便矫限,數(shù)據(jù)適應(yīng)面廣泛哺哼,但是效率有一定降低佩抹。
- 堆空間的分配總是動態(tài)的。
- 操作系統(tǒng)使用stack 段中的指針值訪問heap 段中的對象取董。如果stack 對象的指針沒有了棍苹,則heap 中的對象就不能訪問。這也是內(nèi)存泄露的原因茵汰。
只有OC對象才需要進(jìn)行內(nèi)存管理的原因
- OC對象在內(nèi)存中是以堆的方式分配空間的枢里,并且堆內(nèi)存是由程序員釋放的,就是release蹂午。OC對象存放于堆里面(堆內(nèi)存要程序員手動回收)栏豺。堆里面的內(nèi)存是動態(tài)分配的,所以需要程序員手動的去添加內(nèi)存豆胸、回收內(nèi)存奥洼。
- 非OC對象一般放在棧里面,棧內(nèi)存會被系統(tǒng)自動回收配乱。
stack 對象的優(yōu)點(diǎn)主要有兩點(diǎn)溉卓,一是創(chuàng)建速度快,二是管理簡單搬泥,它有嚴(yán)格的生命周期桑寨。stack 對象的缺點(diǎn)是它不靈活。創(chuàng)建時長度是多大就一直是多 大忿檩,創(chuàng)建時是哪個函數(shù)創(chuàng)建的尉尾,它的owner 就一直是它。不像heap 對象那樣有多個owner 燥透,其實(shí)多個owner 等同于引用計數(shù)沙咏。只有 heap 對象才是采用“引用計數(shù)”方法管理它。
總結(jié)區(qū)別
- 按管理方式分
- 對于棧班套,是由系統(tǒng)編譯器自動管理肢藐,不需要程序員手動管理
- 對于堆,釋放工作由程序員手動管理吱韭,不及時回收容易產(chǎn)生內(nèi)存泄露
- 按分配方式分
- 堆是動態(tài)分配和回收內(nèi)存的吆豹,沒有靜態(tài)分配的堆
- 棧有兩種分配方式:靜態(tài)分配和動態(tài)分配
- 靜態(tài)分配由系統(tǒng)編譯器完成的,比如局部變量的分配
- 動態(tài)分配由alloc函數(shù)進(jìn)行分配的理盆,但是棧的動態(tài)分配和堆不同痘煤,它的動態(tài)分配也由系統(tǒng)編譯器進(jìn)行釋放,不是程序員手動管理猿规。
兩個運(yùn)行內(nèi)存圖
對象的空間分析
值的空間分析
數(shù)據(jù)成員:屬性與實(shí)例變量
類型成員
- 數(shù)據(jù)成員 data member 描述對象狀態(tài)
- 實(shí)例變量 instance variable
- 屬性 propety
- 函數(shù)成員 function member 描述對象行為
- 方法 method
- 初始化器 init
- 析構(gòu)器 dealloc
實(shí)例變量和屬性的區(qū)別
- 大括號括起來的是instance variable(實(shí)例變量)衷快,只是簡單的數(shù)值,不能綁定get/set方法姨俩,不能自動retain/copy/atomic蘸拔。相當(dāng)于一個簡單的跟著instance走的局部變量
- propety則是對get/set方法的語法封裝师郑,將兩個方法的聲明變成一個propety聲明,并且通過標(biāo)注各種attribute來完成許多基本任務(wù)调窍。
屬性認(rèn)識
- 屬性表達(dá)實(shí)例狀態(tài)呕乎,描述類型的對外接口。相比直接訪問實(shí)例變量陨晶,屬性可以做更多的控制。
- 默認(rèn)情況下帝璧,編譯器會為屬性定義propetyName自動合成:
- 一個getter訪問器方法:propertyName
- 一個setter訪問器方法:setPropertyName
- 一個實(shí)例變量_propertyName
- 可以自定義訪問器方法先誉,也可以更改訪問器方法名、或?qū)嵗兞棵?/li>
- 可以使用靜態(tài)全局變量(C語言)+類方法的烁,模擬類型屬性
getter和setter方法
面向?qū)ο蟮恼Z言一般都會有setter和getter器褐耳。在oc中一般這樣寫:
在.h中聲明
-(void)setAge:(int)newAge;
-(int)age;
在.m文件中具體實(shí)現(xiàn)
-(void)setAge:(int)newAge {
age = newAge;
}
-(int)age {
return age;
}
- 調(diào)用方法,一般才用中括號[]和點(diǎn).的方式調(diào)用渴庆。
- 之后引用了@property來改進(jìn)setter和getter铃芦。但是必須聲明與之對應(yīng)的實(shí)例變量。
- 當(dāng)編譯器遇到@property的時候襟雷,會自動展開getter和setter的聲明
- 在m文件中刃滓,@synthesize會自動生成getter和setter的實(shí)現(xiàn)。@synthesize會去訪問str同名的變量耸弄,如果沒有找到就會錯咧虎。(所以必須聲明與之對應(yīng)的實(shí)例變量)
- 之后的版本中,不需要為屬性聲明實(shí)例變量计呈,@synthesize如果找不到會自動生成一個同名的私有同名變量砰诵。
- 再之后的版本中,直接省略@synthesize捌显,因?yàn)榫幾g器可以自動為屬性生成setter和getter方法以及以下劃線開頭的實(shí)例變量_propertyName茁彭。
- 目前,
@property (nonatomic, copy) NSString *str;
這句話完成了3個功能:
1)生成_str成員變量的get和set方法的聲明扶歪;
2)生成_str成員變量set和get方法的實(shí)現(xiàn)理肺;
3)生成一個_str的成員變量。(注意:這種方式生成的成員變量是private的) - 設(shè)置訪問方法的名字击罪,默認(rèn)的名字是setPropertyName和propertyName,可以通過設(shè)置@property的getter和setter屬性來修改setter和getter器的方法名哲嘲。
@property (nonatomic,copy,getter = show1,setter = show2:) NSString *str
屬性重寫setter和getter方法
- 如果只重寫setter和getter其中之一,可以直接重寫
-(void)setStr:(NSString *)str {
_str = @"只重寫setter方法";
}
- 如果要同時重寫setter和getter媳禁。需要加上
@synthesize propertyName = _propertyName;
不然系統(tǒng)不認(rèn)_str
眠副。
如果同時重寫了setter和getter方法,系統(tǒng)就不會幫你自動生成這個_str
竣稽。
@synthesize str = _str;
-(void)setStr:(NSString *)str {
_str = @"同時重寫setter和getter";
}
-(NSString *)str {
return _str;
}
實(shí)例變量
- 可以定義實(shí)例變量囱怕,而不定義屬性霍弹,只有實(shí)例變量沒有類變量
- 如果自定義了getter和setter訪問器方法,或者針對只讀屬性定義了getter訪問器方法娃弓,編譯器將不再合成實(shí)例變量典格,需要自己聲明。
- 在類外一律使用屬性來訪問台丛,類內(nèi)大多也通過self使用屬性訪問耍缴,以下情況使用實(shí)例變量來訪問:
- 初始化器init
- 析構(gòu)器dealloc
- 自定義訪問器方法
- 什么時候使用實(shí)例變量什么時候使用屬性呢?參考這篇文章:iOS鞏基之 不再糾結(jié)實(shí)例變量&屬性
屬性的描述特性
- 讀寫特性
- 讀寫屬性 readwrite 默認(rèn)
- 只讀屬性 readonly
- 多線程特性
- 原子性atomic(默認(rèn))
- 非原子性 nonatomic
- 內(nèi)存管理特性
- ARC環(huán)境
- 強(qiáng)引用strong
- 弱引用 weak 阻止循環(huán)引用
- 拷貝屬性 copy 創(chuàng)建獨(dú)立拷貝
- 其他情況
- retain
- assign
- unsafe_unretained
- ARC環(huán)境
參考這篇文章關(guān)于屬性的特性總結(jié):
1.OC屬性總結(jié)筆記
2.OC 屬性特性assign,retain
函數(shù)成員:方法
- 方法是類的成員函數(shù),表達(dá)實(shí)例行為或類型行為
- 所有方法默認(rèn)為公有方法挽霉,沒有private和protected方法
- 動態(tài)消息分發(fā):方法調(diào)用通過運(yùn)行時動態(tài)消息分發(fā)實(shí)現(xiàn)防嗡,在對象上調(diào)用方法又稱為"向?qū)ο蟀l(fā)送消息"
- OC 的方法調(diào)用就是編譯器通過 objc_msgSend()進(jìn)行消息分發(fā)。下面兩行代碼是等價的:
[array insertObject:foo atIndex:0];
objc_msgSend(array, @selector(insertObject:atIndex:), foo, 0);
- 動態(tài)消息分發(fā)屬于oc runtime機(jī)制侠坎,比較復(fù)雜蚁趁。單獨(dú)在分析。
runtime
- Objective-C語言是一門動態(tài)語言实胸,它將很多靜態(tài)語言在編譯和鏈接時做的事放到了運(yùn)行時來處理他嫡。
- 這種特性意味著OC不僅需要一個編譯器,還需要一個運(yùn)行時系統(tǒng)來執(zhí)行編譯的代碼庐完。對于OC來說钢属,這個運(yùn)行時系統(tǒng)就像一個操作系統(tǒng)一樣。這個運(yùn)行時系統(tǒng)即Runtime门躯,是用C和匯編寫的署咽,這個庫使得C語言有了面向?qū)ο蟮哪芰ΑF渲凶钪饕氖窍C(jī)制生音。對于C語言宁否,函數(shù)的調(diào)用在編譯的會決定調(diào)用哪個函數(shù),編譯完成之后順序執(zhí)行缀遍,無任何二義性慕匠。OC的函數(shù)調(diào)用稱為消息發(fā)送,屬于動態(tài)調(diào)用過程域醇。在編譯的時候并不能決定真正的調(diào)用哪個函數(shù)(事實(shí)證明台谊,在編譯階段,OC可以調(diào)用任何函數(shù)譬挚,即使這個函數(shù)并未實(shí)現(xiàn)锅铅,只要聲明過就不會報錯。而C語言在編譯階段就會報錯减宣。)只有在真正運(yùn)行的時候才會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用盐须。
- Runtime庫主要做下面幾件事:
1.封裝:在這個庫中,對象可以用C語言中的結(jié)構(gòu)體表示漆腌,而方法可以用C函數(shù)來實(shí)現(xiàn)贼邓,另外再加上一些額外的特性阶冈。這些結(jié)構(gòu)體和函數(shù)被Runtime函數(shù)封裝后,我們就可以在程序運(yùn)行時創(chuàng)建塑径、檢查女坑、修改類、對象和他們的方法了统舀。
2.找出方法的最終執(zhí)行代碼:當(dāng)程序執(zhí)行[object doSomething]時匆骗,會向消息接收者(object)發(fā)送一條消息(doSomething),Runtime會根據(jù)消息接收者是否能響應(yīng)該消息而做出不同的反應(yīng)誉简。
實(shí)例方法和類型方法
- 實(shí)例方法的self指針代表當(dāng)前實(shí)例對象绰筛,實(shí)例指針
- 類方法的self指針代表當(dāng)前類
方法參數(shù)
- 注意值類型和引用類型的不同參數(shù)類型對外部的影響。
- 注意方法參數(shù)名描融,內(nèi)部參數(shù)和外部參數(shù),第二個參數(shù)名開始需要顯示的提供外部參數(shù)名衡蚂。
- 調(diào)用時窿克,第一個參數(shù)名忽略,后面的參數(shù)名必須顯示標(biāo)明毛甲。
初始化器和析構(gòu)器
認(rèn)識初始化器和析構(gòu)器
- 初始化器用于初始化對象實(shí)例或類型年叮,是一個特殊的函數(shù)
- 對象初始化器
-(id) init
可以重載多個 - 類型初始化器
+(void)initialize
只能有一個
- 對象初始化器
- 析構(gòu)器用于釋放對象擁有的資源,無返回值的函數(shù)
- 對象析構(gòu)器
-(void)dealloc
只能有一個 - 沒有類型析構(gòu)器
- 對象析構(gòu)器
對象初始化器
初始化對象實(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相等于調(diào)用alloc/init的無參數(shù)版本。
初始化器內(nèi)部使用實(shí)例變量不使用屬性七咧。
id或者instancetype類型的返回值跃惫。
指定初始化器(真正實(shí)現(xiàn)的初始化),便捷初始化器(會調(diào)用指定初始化器)?
指定初始化方法和便利構(gòu)造器
指定初始化方法
- 無論調(diào)用哪一個初始化方法都會調(diào)用的初始化方法艾栋,稱為指定初始化方法爆存。
- 一般會寫一個傳入?yún)?shù)最多的init方法,然后其他方法調(diào)用這個指定初始化方法即可蝗砾。
- (id)initWithName:(NSString *)name
{
//調(diào)用指定初始化方法
return [self initWithName:name age:0 sex:'0' height:0];
}
//指定初始化方法
- (id)initWithName:(NSString *)name age:(NSInteger)age sex:(char)sex height:(CGFloat)height
{
self = [super init];
if (self) {
_name = name;
_age = age;
_sex = sex;
_height = height;
}
return self;
}
便利構(gòu)造器
- 便利構(gòu)造器就是一個類方法(+號方法)先较,內(nèi)部實(shí)現(xiàn)是封裝了alloc和初始化操作,創(chuàng)建對象更加方便快捷悼粮。
- 定義便利構(gòu)造器有以下規(guī)則:
- 便利構(gòu)造器是“+”方法闲勺。
- 返回本類型的實(shí)例。
- ?法名以類名開頭扣猫。
- 可以有0到多個參數(shù)菜循。
+(id)PersonWithName:(NSString *)name age:(NSInteger)age sex:(char)sex height:(CGFloat)height
{
Person *p = [[Person alloc] initWithName:name age:age sex:sex height:height];
return p;
}
類初始化器
- 類初始化器initialize負(fù)責(zé)類型級別的初始化
- initialize在每個類使用之前被系統(tǒng)自動調(diào)用,且每個進(jìn)程周期中申尤,只被調(diào)用一次债朵。
- 子類的initialize會自動調(diào)用父類的initialize(前置調(diào)用)
對象析構(gòu)器
- 對象析構(gòu)器dealloc負(fù)責(zé)釋放對象擁有的動態(tài)資源
- 自動實(shí)現(xiàn):ARC將對象屬性引用計數(shù)減持
- 手動實(shí)現(xiàn):釋放不受ARC管理的動態(tài)內(nèi)存子眶,如malloc分配的內(nèi)存
- 手動實(shí)現(xiàn):關(guān)閉非內(nèi)存資源,如文件句柄序芦,網(wǎng)絡(luò)端口
- dealloc由ARC根據(jù)對象引用計數(shù)規(guī)則臭杰,在釋放對象內(nèi)存前自動調(diào)用,無法手工調(diào)用
- 子類的dealloc會自動調(diào)用父類的dealloc(后置調(diào)用)
繼承與多態(tài)
認(rèn)識面向?qū)ο?/h3>
- 封裝 encapsulation
- 繼承 inheritance
- 多態(tài) polymorphism
繼承
- 繼承:每一個類只能有一個基類谚中,子類自動繼承基類
- 實(shí)例變量
- 屬性
- 實(shí)例方法
- 類方法
- 所有類的根類:NSObject
- 繼承的兩層含義:
- 成員復(fù)用:子類復(fù)用基類成員
- 類型抽象:將子類當(dāng)做父類來使用
運(yùn)行時多態(tài) polymorphism
- 多態(tài):子類在父類統(tǒng)一行為接口下渴杆,表現(xiàn)不同的實(shí)現(xiàn)方式
- 對比重寫和重載
- 子類重寫父類同名同參數(shù)方法
- 方法名相同、參數(shù)不同宪塔,OC不支持方法的重載
- 在子類的代碼中磁奖,可以使用super來調(diào)用基類的實(shí)現(xiàn)
- self具有多態(tài)性,可以指向不同的子類
- super沒有多態(tài)性某筐,僅指向當(dāng)前父類
繼承中的init和dealloc
- 初始化器init
- 子類自動繼續(xù)基類的初始化器
- 子類也可以重寫基類初始化器比搭,此時子類初始化器必須首先調(diào)用基類的一個初始化器(手工調(diào)用)
- 析構(gòu)器dealloc
- 子類可以選擇繼承基類析構(gòu)器,或者重寫基類析構(gòu)器
- 子類析構(gòu)器執(zhí)行完畢后南誊,會自動調(diào)用基類析構(gòu)器(后置調(diào)用身诺,且不支持手工調(diào)用)
- 子類析構(gòu)器自動具有多態(tài)性
- Tips:盡量避免在父類init和dealloc調(diào)用子類重寫的方法。
- 實(shí)例變量
- 屬性
- 實(shí)例方法
- 類方法
- 成員復(fù)用:子類復(fù)用基類成員
- 類型抽象:將子類當(dāng)做父類來使用
- 子類重寫父類同名同參數(shù)方法
- 方法名相同、參數(shù)不同宪塔,OC不支持方法的重載
- self具有多態(tài)性,可以指向不同的子類
- super沒有多態(tài)性某筐,僅指向當(dāng)前父類
- 子類自動繼續(xù)基類的初始化器
- 子類也可以重寫基類初始化器比搭,此時子類初始化器必須首先調(diào)用基類的一個初始化器(手工調(diào)用)
- 子類可以選擇繼承基類析構(gòu)器,或者重寫基類析構(gòu)器
- 子類析構(gòu)器執(zhí)行完畢后南誊,會自動調(diào)用基類析構(gòu)器(后置調(diào)用身诺,且不支持手工調(diào)用)
- 子類析構(gòu)器自動具有多態(tài)性
在我的博客中抄囚,點(diǎn)我