Objective-C
1. import的用法
- 拷貝文件內(nèi)容
可以自動(dòng)防止文件的內(nèi)容被重復(fù)拷貝(#define宏定義) - Foundation 框架頭文件的路徑
Xcode.app 顯示包內(nèi)容
Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/Frameworks/Foundation.framework - 命令行指令
- 編寫 Oc 源文件: .m .c
- 編譯: cc -c xxx.m xxx.c
- 鏈接: cc xxx.o xxx.o -framework Foundation(用到的時(shí)候才加)
- 運(yùn)行: ./a.out
- 主頭文件
主頭文件:最主要的頭文件蜒茄,名字一般跟框架名稱一樣,包含了框架中的所有其他頭文件 - Foundation框架的主頭文件名稱就是Foundation.h
只需要包含F(xiàn)oundation框架主頭文件士修,就可以使用整個(gè)框架的東西
2. 對(duì)象方法和類方法
- 對(duì)象方法: - 開(kāi)頭
- 只能由對(duì)象調(diào)用
- 對(duì)象方法中能訪問(wèn)當(dāng)前對(duì)象的成員變量(實(shí)例變量)
- 類方法: + 開(kāi)頭
- 只能由類名來(lái)調(diào)用
- 類方法中不能訪問(wèn)成員變量
- 類方法的優(yōu)點(diǎn)和使用場(chǎng)合:
- 不依賴于對(duì)象,執(zhí)行效率高
- 能用類方法,盡量用類方法
- 場(chǎng)合:當(dāng)方法內(nèi)部不需要使用到成員變量時(shí),就用類方法
- 可以允許類方法和對(duì)象方法同名
3. 成員變量和局部變量
-
成員變量:
- 寫在類聲明的大括號(hào)中的變量, 我們稱之為 成員變量(屬性, 實(shí)例變量)成員變量只能通過(guò)對(duì)象來(lái)訪問(wèn)
- 注意: 成員變量不能離開(kāi)類,倔矾,離開(kāi)類之后就不是成員變量掸驱,成員變量不能在定義的同時(shí)進(jìn)行初始化
- 存儲(chǔ): 堆(當(dāng)前對(duì)象對(duì)應(yīng)的堆的存儲(chǔ)空間中)
- 存儲(chǔ)在堆中的數(shù)據(jù), 不會(huì)被自動(dòng)釋放, 只能程序員手動(dòng)釋放
局部變量:
-
寫在函數(shù)或者代碼塊中的變量, 我們稱之為局部變量
- 作用域: 從定義的那一行開(kāi)始, 一直到遇到大括號(hào)或者return
- 局部變量可以先定義再初始化, 也可以定義的同時(shí)初始化
- 存儲(chǔ) : 棧
- 存儲(chǔ)在棧中的數(shù)據(jù)有一個(gè)特點(diǎn), 系統(tǒng)會(huì)自動(dòng)給我們釋放
-
全局變量
- 寫在函數(shù)和大括號(hào)外部的變量, 我們稱之為全局變量
- 作用域: 從定義的那一行開(kāi)始, 一直到文件末尾
- 局部變量可以先定義在初始化, 也可以定義的同時(shí)初始化
- 存儲(chǔ): 靜態(tài)區(qū)
- 程序一啟動(dòng)就會(huì)分配存儲(chǔ)空間, 直到程序結(jié)束才會(huì)釋放
4. 方法和函數(shù)
- 方法
- 函數(shù)屬于整個(gè)文件,, 方法屬于某一個(gè)類,方法如果離開(kāi)類就不行
- 函數(shù)可以直接調(diào)用, 方法必須用對(duì)象或者類來(lái)調(diào)用
- 函數(shù)
- 能寫在文件中的任意位置(@interface和@end之間除外)株茶,函數(shù)歸文件所有
- 函數(shù)調(diào)用不依賴于對(duì)象
- 函數(shù)內(nèi)部不能直接通過(guò)成員變量名訪問(wèn)某個(gè)對(duì)象的成員變量
5. setter 和 getter 用法簡(jiǎn)介
- setter
- 作用: 給成員變量賦值
- 格式:
- 必須是對(duì)象方法
- 一定沒(méi)有返回值
- 方法名稱一定以set開(kāi)頭, set后面跟上成員變量的名稱, 并去掉下劃線, 然后將首字母大寫
- 一定有參數(shù), 并且參數(shù)類型和成員變量的類型一致, 參數(shù)名稱就是成員變量的名稱去掉下劃線
- getter
- 作用: 返回成員變量的值
- 格式:
- 必須是對(duì)象方法
- 一定有返回值, 返回值類型和成員變量的類型一致
- 方法名稱就是成員變量的名稱去掉下劃線
- 一定沒(méi)有參數(shù)
- readLoad 和 readWrite
- 一個(gè)屬性可以只有g(shù)etter方法, 沒(méi)有setter方法, 這種屬性我們稱之為只讀屬性
- 一個(gè)屬性也可以只有setter方法, 沒(méi)有g(shù)etter方法, 這種屬性我們稱之為只寫屬性
- 如果既有setter方法又有g(shù)etter方法, 那么這種屬性我們稱之為可讀可寫的屬性
- 一個(gè)屬性也可以沒(méi)有g(shù)etter和setter, 這種屬性我們稱之為私有屬性
6. self 的基本使用
- 用法
- 那個(gè)調(diào)用了當(dāng)前方法,self就代表誰(shuí)
- self出現(xiàn)在對(duì)象方法中, self就代表對(duì)象
- self出現(xiàn)在類方法中, self就代表類
- 在對(duì)象方法利用 "self->成員變量名” 訪問(wèn)當(dāng)前對(duì)象內(nèi)部的成員變量(實(shí)例變量)
- [self 方法名] 可以調(diào)用其他對(duì)象方法\類方法
- self會(huì)自動(dòng)區(qū)分類方法和對(duì)象方法, 如果在類方法中使用self調(diào)用對(duì)象方法, 那么會(huì)直接報(bào)錯(cuò)
- 不能在對(duì)象方法或者類方法中利用self調(diào)用當(dāng)前self所在的方法
- 動(dòng)態(tài)綁定:
動(dòng)態(tài)類型能使程序直到執(zhí)行時(shí)才確定對(duì)象的真實(shí)類型
動(dòng)態(tài)類型綁定能使程序直到執(zhí)行時(shí)才確定要對(duì)那個(gè)對(duì)象調(diào)用的方法 - 使用self調(diào)用本方法恕洲,導(dǎo)致死循環(huán)調(diào)用
- #pragma mark
- 將代碼分隔開(kāi)塔橡,方便我們進(jìn)行查找。
7. super 基本使用
- 編譯器指令符號(hào).
- 利用super給父類的方法發(fā)送一個(gè)消息, 那么系統(tǒng)就會(huì)自動(dòng)調(diào)用父類的方法
- 如果以后想在子類中調(diào)用父類的方法可以使用super
- 如果想在給父類方法進(jìn)行擴(kuò)展的同時(shí)保留父類的方法, 那么可以使用super調(diào)用父類同名的方法
- super 在什么方法中就調(diào)用父類的什么方法
8. 面向?qū)ο蠡舅枷?/h2>
封裝
- 原理:屏蔽內(nèi)部實(shí)現(xiàn)的細(xì)節(jié)霜第,僅僅對(duì)外提供共有的方法/接口
- 好處:保證數(shù)據(jù)的安全性
- 規(guī)范:一般情況下不會(huì)對(duì)外直接暴露成員變量, 都會(huì)提供一些共有的方法進(jìn)行賦值成員變量都需要封裝起來(lái)
繼承
- 父類必須聲明在子類的前面
- 不允許子類和父類擁有相同名稱的成員變量, 因?yàn)樽宇惱^承父類谱邪,子類將會(huì)擁有父類的所有成員變量,若在子類中定義父類同名成員變量 屬于重復(fù)定義庶诡。
- 調(diào)用某個(gè)對(duì)象的方法時(shí), 優(yōu)先去當(dāng)前類中找, 如果么有, 去父類中找
- 基類的私有屬性能被繼承, 不能在子類中訪問(wèn)惦银。
- OC中的繼承是單繼承:也就是說(shuō)一個(gè)類只能一個(gè)父類, 不能繼承多個(gè)父類
- 缺點(diǎn):耦合性太強(qiáng)
多態(tài)
- 事物的多種形態(tài)
- 沒(méi)有繼承就沒(méi)有多態(tài)
- 代碼的體現(xiàn): 父類類型的指針指向子類對(duì)象
- 好處: 如果函數(shù)\方法參數(shù)中使用的是父類類型, 可以傳入父類, 子類對(duì)象
- 局限性: 父類類型的變量 不能 直接調(diào)用子類特有的方法,必須強(qiáng)轉(zhuǎn)為子類類型變量后, 才能直接調(diào)用子類特有的方法
- 動(dòng)態(tài)綁定:
- 動(dòng)態(tài)類型能使程序直到執(zhí)行時(shí)才確定對(duì)象的真實(shí)類型
- 動(dòng)態(tài)類型綁定能使程序直到執(zhí)行時(shí)才確定要對(duì)那個(gè)對(duì)象調(diào)用的方法
假設(shè) 子類 Dog 有一個(gè)特有的方法bark
[dog bark];
Animal *an = [Dog new];
[(Dog*)an bark]; //把父類的指針,強(qiáng)制類型轉(zhuǎn)換
9. 成員變量的作用域
- @public 在任何地方都能直接訪問(wèn)對(duì)象的成員變量
- @private 只能在當(dāng)前類的對(duì)象方法中直接訪問(wèn)(子類可以通過(guò)seter geter方法訪問(wèn)父類的私有的成員變量)
- @protected 能在當(dāng)前類和子類的對(duì)象方法中直接訪問(wèn) (默認(rèn)是 protected)
- @package 只要處在同一個(gè)框架中, 就能直接訪問(wèn)對(duì)象的成員變量(不常用)
點(diǎn)語(yǔ)法使用注意
- 點(diǎn)語(yǔ)法的本質(zhì)還是方法調(diào)用
p.age = 10; // [p setAge:10]
- 引發(fā)死循環(huán)
self.age = age; // [self setAge:age]
- 私有成員變量
- 寫在@implementation 中的成員變量,默認(rèn)就是私有成員變量扯俱,并且和利用@private 修飾的不太一樣书蚪,@implementation 中定義的成員變量在其他類中無(wú)法查看,也無(wú)法訪問(wèn)
在@implementation 中定義的私有變量只能在本類中查看
- 私有方法:只有實(shí)現(xiàn)沒(méi)有聲明迅栅,OC 中美有真正的私有方法殊校,因?yàn)?OC 是消息機(jī)制。私有方法外面不能訪問(wèn)读存,只能通過(guò)包裝成 sel 就可以訪問(wèn)
10. @property 用法
Property 編譯器指令
-
生成setter 和 getter 方法聲明(未加強(qiáng)版)
-(void)setAge:(int)age;
-(int)age;
@property int age;
@synthesize age = _age;
setter和getter實(shí)現(xiàn)中會(huì)訪問(wèn)成員變量_age, 如果成員變量_age不存在为流,就會(huì)自動(dòng)生成一個(gè)@private的成員變量_age
@synthesize age;
setter和getter實(shí)現(xiàn)中會(huì)訪問(wèn)@synthesize后同名成員變量age
如果成員變量age不存在,就會(huì)自動(dòng)生成一個(gè)@private的成員變量age
-
多個(gè)屬性可以通過(guò)一行@synthesize搞定,多個(gè)屬性之間用逗號(hào)連接
@synthesize age = _age, number = _number, name = _name;
Property 增強(qiáng)
只要利用一個(gè)@property就可以同時(shí)生成setter/getter方法的聲明和實(shí)現(xiàn)傳入的屬性賦值給_開(kāi)頭的成員變量
@property有一個(gè)弊端: 它只會(huì)生成最簡(jiǎn)單的getter/setter方法的聲明和實(shí)現(xiàn), 并不會(huì)對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾
如果想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 那么我們就必須重寫getter/setter方法如果不想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 僅僅是提供一個(gè)方法給外界操作成員變量, 那么就可以使用@property
注意: 如果沒(méi)有會(huì)自動(dòng)生成一個(gè)_開(kāi)頭的成員變量,自動(dòng)生成的成員變量是私有變量, 聲明在.m中,在其它文件中無(wú)法查看,但當(dāng)可以在本類中查看
-
有就不生成让簿,沒(méi)有就生成
- 如果重寫了setter方法, 那么property就只會(huì)生成getter方法
- 如果重寫了getter方法, 那么property就只會(huì)生成setter方法
- 如果同時(shí)重寫了getter/setter方法, 那么property就不會(huì)自動(dòng)幫我們生成私有的成員變量
@property(屬性修飾符) 數(shù)據(jù)類型 變量名稱;
readwrite:代表生成 getter 和 setter 方法敬察,默認(rèn)就是
readonly:代表只生成 getter 方法,(只讀)
修改 getter 方法名(常用)
程序員之間有一個(gè)約定, 一般情況下獲取BOOL類型的屬性的值, 我們都會(huì)將獲取的方法名稱改為isXXX
11. new alloc init 的基本用法及區(qū)別
- alloc
- 開(kāi)辟存儲(chǔ)空間
- 將所有成員變量設(shè)為0
- 返回當(dāng)前的對(duì)象地址
- init
- 初始化成員變量, 但是默認(rèn)情況下init的實(shí)現(xiàn)是什么都沒(méi)有做 2.返回初始化后的實(shí)例對(duì)象地址
- alloc和 init 返回的地址是一樣的
12. id和 instancetype
- 靜態(tài)類型和動(dòng)態(tài)類型
- 靜態(tài)類型:將一個(gè)指針變量定義為特定類的對(duì)象時(shí),使用的是靜態(tài)類型,在編譯的時(shí)候就知道這個(gè)指針變量所屬的類,這個(gè)變量總是存儲(chǔ)特定類的對(duì)象尔当。
Person *p = [Person alloc] init]]
- 動(dòng)態(tài)類型:這一特性是程序直到執(zhí)行時(shí)才確定對(duì)象所屬的類
id p = [[Person alloc] init];
- Id
- id 是一種通用的對(duì)象類型,它可以指向?qū)儆谌魏晤惖膶?duì)象,也可以理解為萬(wàn)能指針
- id是動(dòng)態(tài)類型,所以可以通過(guò)id類型直接調(diào)用指向?qū)ο笾械姆椒? 編譯器不會(huì)報(bào)錯(cuò)
- 優(yōu)點(diǎn)
- 通過(guò)靜態(tài)數(shù)據(jù)類型定義變量, 不能調(diào)用子類特有的方法
- 通過(guò)動(dòng)態(tài)數(shù)據(jù)類型定義變量, 可以調(diào)用子類特有的方法
- 通過(guò)動(dòng)態(tài)數(shù)據(jù)類型定義的變量, 可以調(diào)用私有方法
- 弊端: 由于動(dòng)態(tài)數(shù)據(jù)類型可以調(diào)用任意方法, 所以有可能調(diào)用到不屬于自己的方法, 而編譯時(shí)又不會(huì)報(bào)錯(cuò), 所以可能導(dǎo)致運(yùn)行時(shí)的錯(cuò)誤
- 應(yīng)用場(chǎng)景
- 多態(tài), 可以減少代碼量, 避免調(diào)用子類特有的方法需要強(qiáng)制類型轉(zhuǎn)換
- 為了避免動(dòng)態(tài)數(shù)據(jù)類型引發(fā)的運(yùn)行時(shí)的錯(cuò)誤, 一般情況下如果使用動(dòng)態(tài)數(shù)據(jù)類型定義一個(gè)變量, 在調(diào)用這個(gè)變量的方法之前會(huì)進(jìn)行一次判斷, 判斷當(dāng)前變量是否能夠調(diào)用這個(gè)方法
id obj = [Student new];
[obj isKindOfClass:[Student class]]
//isKindOfClass , 判斷指定的對(duì)象是否是某一個(gè)類, 或者是某一個(gè)類的子類
- instancetype
- instancetype == id == 萬(wàn)能指針 == 指向一個(gè)對(duì)象
- id在編譯的時(shí)候不能判斷對(duì)象的真實(shí)類型
- instancetype在編譯的時(shí)候可以判斷對(duì)象的真實(shí)類型
- id和instancetype除了一個(gè)在編譯時(shí)不知道真實(shí)類型, 一個(gè)在編譯時(shí)知道真實(shí)類型以外, 還有一個(gè)區(qū)別
- id可以用來(lái)定義變量, 可以作為返回值, 可以作為形參
- instancetype只能用于作為返回值
- 注意: 以后但凡自定義構(gòu)造方法, 返回值盡量使用instancetype, 不要使用id
13. 構(gòu)造方法(- 開(kāi)頭的對(duì)象方法)
用來(lái)初始化對(duì)象的方法.
重寫構(gòu)造方法的注意:
先調(diào)用父類的構(gòu)造方法.
再進(jìn)行子類內(nèi)部的成員變量的初始化
-
返回當(dāng)前對(duì)象的地址
-(instancetype)init
{
// 注意: 不要把 = 號(hào)寫為 ==
// 一定要將[super init]的返回值賦值給self
if (self = [super init]) {
// 初始化子類
_age = 6;
}
return self;
}
自定義構(gòu)造方法
自己做自己的事情
父類的屬性交給父類的方法來(lái)處理,子類的方法處理子類自己獨(dú)有的屬性
自定義構(gòu)造方法必須以intiWith開(kāi)頭,并且’W’必須大寫
類工廠方法:
用于快速創(chuàng)建對(duì)象的類方法, 我們稱之為類工廠方法
類工廠方法中主要用于 給對(duì)象分配存儲(chǔ)空間和初始化這塊存儲(chǔ)空間
-
規(guī)范:
- 一定是類方法 +
- 方法名稱以類的名稱開(kāi)頭, 首字母小寫
- 一定有返回值, 返回值是id/instancetype
- 注意: 以后但凡自定義類工廠方法, 在類工廠方法中創(chuàng)建對(duì)象一定不要使用類名來(lái)創(chuàng)建莲祸,
一定要使用self來(lái)創(chuàng)建
return [[self alloc] init];
14. Category - 分類
- 在不改變?cè)瓉?lái)類模型的前提下, 給類擴(kuò)充一些方法. 有2種方式 : 繼承 分類
- 好處: 一個(gè)龐大的類可以分模塊開(kāi)發(fā), 一個(gè)龐大的類可以由多個(gè)人來(lái)編寫, 便于團(tuán)隊(duì)合作.
- 使用注意:
- 分類中寫property, 只會(huì)生成getter/setter方法的聲明, 不會(huì)生成實(shí)現(xiàn)和私有成員變量
- Category 可以
訪問(wèn)原始類的成員變量
, 但不能添加變量, 只能添加方法. 如果想 添加變量, 可以考慮通過(guò)繼承創(chuàng) 建子類
- Category 可以實(shí)現(xiàn)原始類的方法, 不推薦這么做, 因?yàn)樗翘鎿Q掉原始類的方 法, 這么做以后就不能訪問(wèn)原來(lái)的 方法.
- 多個(gè)Category 中如果實(shí)現(xiàn)了相同的方法, 只有最后一個(gè)參與編譯的才會(huì)有效.
- 方法調(diào)用的優(yōu)先級(jí) : 分類(最后參與編譯的分類優(yōu)先) —>原來(lái)類—>父類
- 類擴(kuò)展(Extendsion)
- 某個(gè)類擴(kuò)充一些私有的成員變量和方法
- 寫在.m文件中
- 英文名是Class Extension
- 格式(俗稱匿名分類)
@interface 類名 ()
@end
15. 類的本質(zhì)(typedef struct objc_class * Class)
- 類也是一個(gè)對(duì)象, 是 class 類型的對(duì)象, 簡(jiǎn)稱 “類對(duì)象”, 類名就代表著類對(duì)象, 每個(gè)類只有一個(gè)類對(duì)象
- + load
- + load : 在程序啟動(dòng)的時(shí)候會(huì)加載所有的類和分類, 并調(diào)用所有類和分類的 + load 方法,并且只會(huì)調(diào)用一次椭迎。
- 加載順序
父類 → 子類→ 分類
锐帜。不管程序運(yùn)行過(guò)程有沒(méi)有用到這個(gè)類, 都會(huì)調(diào)用 + load 方法。
- + initialize
- + initialize : 在第一次使用某個(gè)類時(shí)(比如創(chuàng)建對(duì)象等), 且只會(huì)調(diào)用一次 + initialize 方法.
- 主要用于對(duì)某一個(gè)類一次性初始化
- 一個(gè)類只會(huì)調(diào)用一次 + initialize方法, 先調(diào)用父類的, 再調(diào)用子類的
- 獲取類對(duì)象的2種方式(獲取內(nèi)存中的類對(duì)象)
```
Class c = [Person class] // 類方法
Person *p = [Person new];
Class c1 = [p class]; // 對(duì)象方法
```
- 類在內(nèi)存的表現(xiàn)
- 實(shí)例對(duì)象 → 類對(duì)象(對(duì)象方法)→ 元類對(duì)象(類方法)→ 根元類 (isa 指向自己)
- 元類保存了類方法的列表畜号。當(dāng)一個(gè)類方法被調(diào)用時(shí),元類會(huì)首先查找它本身是否有該類方法的實(shí)現(xiàn),如果沒(méi)有則該元類會(huì)向它的父類查找該方法,直到一直找到繼承鏈的頭缴阎。
- 元類(metaclass)也是一個(gè)對(duì)象,那么元類的isa指針又指向哪里呢?為了設(shè)計(jì)上的完整,所有的元類的isa指針都會(huì)指向一個(gè)根元類(root metaclass)。
- 根元類(root metaclass)本身的isa指針指向自己,這樣就行成了一個(gè)閉環(huán)简软。上面說(shuō)到,一個(gè)對(duì)象能夠接收的消息列表是保存在它所對(duì)應(yīng)的類中的药蜻。在實(shí)際編程中,我們幾乎不會(huì)遇到向元類發(fā)消息的情況,那它的isa 指針在實(shí)際上很少用到。不過(guò)這么設(shè)計(jì)保證了面向?qū)ο蟮母蓛?即所有事物都是對(duì)象,都有isa指針替饿。
- 由于類方法的定義是保存在元類(metaclass)中,而方法調(diào)用的規(guī)則是,如果該類沒(méi)有一個(gè)方法的實(shí)現(xiàn),則向它的父類繼續(xù)查找语泽。所以為了保證父類的類方法可以在子類中可以被調(diào)用,所以子類的元類會(huì)繼承父類的元類,換而言之,類對(duì)象和元類對(duì)象有著同樣的繼承關(guān)系。
-
如下圖 :
16. NSLog
- 使用 NSLog 和 %@輸出某個(gè)類對(duì)象時(shí), 會(huì)調(diào)用類對(duì)象 + description 方法, 并拿到返回值(NSString *)進(jìn)行輸出
- - description方法默認(rèn)返回對(duì)象的描述信息(默認(rèn)實(shí)現(xiàn)是返回類名和對(duì)象的內(nèi)存地址)
- description方法是基類NSObject 所帶的方法. 使用NSLog輸出OC對(duì)象,意義就不是很大,因?yàn)槲覀儾⒉魂P(guān)心對(duì)象的內(nèi)存地址,比較關(guān)心的是對(duì)象內(nèi)部的一些成變量的值视卢。因此,會(huì)經(jīng)常重寫description方法,覆蓋description方法 的默認(rèn)實(shí)現(xiàn)
-(NSString *) description
{
return [NSString stringWithFormat:@"age = %d", _age]
}
- + descrption 方法
- 當(dāng)使用NSLog輸出該類的類對(duì)象的時(shí)候調(diào)用*/(
不常用
)
- 注意: 死循環(huán). 如果在 - description方法中使用 NSLog %@ 輸出self對(duì)象會(huì)引發(fā)死循環(huán)
17. SEL 基本使用
代表方法的簽名踱卵,在類對(duì)象的方法列表
中存儲(chǔ)著該簽名與方法代碼的對(duì)應(yīng)關(guān)系
-
每個(gè)方法都有一個(gè)與之對(duì)應(yīng)的 SEL類型的對(duì)象
- SEL 其實(shí)是對(duì)方法的一種包裝, 將方法包裝成一個(gè) SEL 類型的數(shù)據(jù), 去找對(duì)應(yīng)的方法地址, 進(jìn)而進(jìn)行調(diào)用
- 注意:在這個(gè)操作過(guò)程中又緩存,第一次找的時(shí)候一個(gè)一個(gè)的找据过,非常耗性能惋砂,之后再用到的時(shí)候就直接使用
-
對(duì)象是否實(shí)現(xiàn)了某個(gè)方法
- - (BOOL) respondsToSelector: (SEL)selector 判斷實(shí)例是否實(shí)現(xiàn)這樣方法
- + (BOOL)instancesRespondToSelector:(SEL)aSelector; (類對(duì)象)
讓對(duì)象執(zhí)行某個(gè)方法
- (id)performSelector:(SEL)aSelector;
-
SEL 類型的定義 typedef struct objc_selector *SEL
//SEL 對(duì)象的創(chuàng)建
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test”);
// 將SEL對(duì)象轉(zhuǎn)為NSString對(duì)象
NSString *str = NSStringFromSelector(@selector(test));
Person *p = [Person new];
// 每個(gè)類都有以個(gè)_cmd 代表當(dāng)前方法
// 調(diào)用對(duì)象p的test方法
[p performSelector : @selector (test)];
18. 內(nèi)存管理
Automatic Reference Couting
- 什么是自動(dòng)引用計(jì)數(shù)器
- 每個(gè)OC對(duì)象都有自己的引用計(jì)數(shù)器绳锅,它是一個(gè)整數(shù)西饵,從字面上, 可以理解為”對(duì)象被引用的次數(shù)”
- 也可以理解為: 它表示有多少人正在用這個(gè)對(duì)象
占4個(gè)字節(jié)
Manul Refrence Counting
-
什么是手動(dòng)引用計(jì)數(shù)?
- 所有對(duì)象的內(nèi)容都需要我們手動(dòng)管理, 需要程序員自己編寫release/retain等代碼
-
方法的基本使用
- retain : 計(jì)數(shù)器 +1 , 會(huì)返回對(duì)象本身
- release : 計(jì)數(shù)器 -1 , 沒(méi)有返回值(
release并不代表銷毀對(duì)象, 僅僅是計(jì)數(shù)器-1
)
- retainCount : 獲取當(dāng)前的計(jì)數(shù)器
-
概念
- 僵尸對(duì)象: 所占用內(nèi)存已經(jīng)被回收的對(duì)象, 僵尸對(duì)象不能再使用
- 野指針: 指向僵尸對(duì)象(不可用內(nèi)存)的指針, 給野指針發(fā)送消息會(huì)報(bào)錯(cuò)(EXC_BAD_ACCES)
- 空指針: 沒(méi)有指向任何東西的指針(儲(chǔ)存的東西是nil NULL 0), 給空指針發(fā)送消息不會(huì)報(bào)錯(cuò)
- 內(nèi)存管理代碼規(guī)范
- 只要調(diào)用了alloc, 必須有relese(autorelease), 如果對(duì)象不是通過(guò)alloc產(chǎn)生的, 就不需要release
- set方法的代碼規(guī)范
- 基本數(shù)據(jù)類型: 直接復(fù)制
-(void)setAge:(int)age
{
_age = age;
}
```
- OC對(duì)象類型
```
-(void)setCar:(Car *)car
{
// 先判斷是不是新傳進(jìn)來(lái)對(duì)象
if( car != _car)
{
// 對(duì)舊對(duì)象做一次release
[_car release]
// 對(duì)新對(duì)象做一次retain
_car = [car retain]
}
}
```
- dealloc方法的代碼規(guī)范
- 對(duì)self(當(dāng)前)所擁有的其他對(duì)象做一次release
- 當(dāng)一個(gè)對(duì)象要被回收的時(shí)候, 就會(huì)調(diào)用
- 一定要調(diào)用
[super dealloc]
, 這句調(diào)用放在最后面
@Property 參數(shù)
- set 方法內(nèi)存管理相關(guān)的參數(shù)
- retain : release 舊值 , retain 新值 (適用于OC對(duì)象類型)
- assign : 直接賦值(默認(rèn), 適用于非OC對(duì)象類型)
- copy : release 舊值, copy 新值
- 是否要生成set方法
- readwrite : 同時(shí)生成setter 和 getter的聲明, 實(shí)現(xiàn)(默認(rèn))
- readonly : 只會(huì)生成getter的聲明, 實(shí)現(xiàn)
- 多線程管理
- nonatomic : 性能高 (一般就用這個(gè))
- atomic : 性能低(默認(rèn))
- setter 和 getter方法的名稱
- setter : 決定了set方法的名稱, 一定要有個(gè)冒號(hào) :
- getter : 決定了get方法的名稱(一般用在BOOL類型)
@Class(循環(huán)引用)
僅僅告訴編譯器,某個(gè)名稱是一個(gè)類
-
開(kāi)發(fā)中引用一個(gè)類的規(guī)范
- 在.h 文件中用@class 來(lái)聲明類
- 在.m 文件中用#import 來(lái)包含類的所有東西
-
和#import 的區(qū)別(面試)
- import會(huì)包含引用類的所有信息(內(nèi)容),包括引用類的變量和方法
- @class僅僅是告訴編譯器有這么一個(gè)類, 具體這個(gè)類里有什么信息, 完全不知
-
總結(jié):
- 如果都在.h中import, 假如A拷貝了B, B拷貝了C , 如果C被修改了, 那么B和A都需要重新拷貝. 因?yàn)镃修改了那么B就會(huì)重新拷貝, 而B重新拷貝之后相當(dāng)于B也被修改了, 那么A也需要重新拷貝. 也就是說(shuō)如果都在.h中拷貝, 只要有間接關(guān)系都會(huì)重新拷貝
- 如果在.h中用@class, 在.m中用import, 那么如果一個(gè)文件發(fā)生了變化, 只有和這個(gè)文件有直接關(guān)系的那個(gè)文件才會(huì)重新拷貝
- 所以在.h中用@class可以提升編譯效率
兩端循環(huán)引用(面試)
retain
* 比如A對(duì)象retain了B對(duì)象鳞芙,B對(duì)象retain了A對(duì)象眷柔,這樣會(huì)導(dǎo)致A對(duì)象和B對(duì)象永遠(yuǎn)無(wú)法釋放期虾。
* 當(dāng)兩端互相引用時(shí),應(yīng)該一端用retain驯嘱、一端用assign镶苞。
import
* 如果兩個(gè)類相互(#import<>
)拷貝, 例如A拷貝B, B拷貝A, 這樣會(huì)報(bào)錯(cuò)
- 如何解決: 在.h中用@class, 在.m中用import
- 因?yàn)槿绻?h中都用import, 那么A拷貝B, B又拷貝A, 會(huì)形成死循環(huán)
- 如果在.h中用@class, 那么不會(huì)做任何拷貝操作, 而在.m中用import只會(huì)拷貝對(duì)應(yīng)的文件, 并不會(huì)形成死循環(huán)
@ autorelease基本用法
- 會(huì)將對(duì)象放到一個(gè)自動(dòng)釋放池中,并且會(huì)返回對(duì)象本身
- 當(dāng)自動(dòng)釋放池被銷毀時(shí)鞠评, 會(huì)對(duì)池子里面的所有對(duì)象做一次release 操作
- 調(diào)用完@autorelease 方法后茂蚓,對(duì)象計(jì)數(shù)器不變
- @autorelease 的好處
- 不用關(guān)心對(duì)象釋放的時(shí)間
- 不用關(guān)心什么時(shí)候調(diào)用 release
- @autorelease 使用注意
- 占用內(nèi)存較大的對(duì)象不要隨便用 autorelease
- 占用內(nèi)存較小的對(duì)象使用 autorelease,沒(méi)有太大影響(影響:不能控制對(duì)象的釋放時(shí)間)
- 錯(cuò)誤寫法
- alloc 之后調(diào)用了 autorelease 剃幌,又調(diào)用 release
Person *p = [[[Person alloc] init] autorelease];
[p release];
- 連續(xù)調(diào)用autorelease(野指針錯(cuò)誤聋涨,每個(gè)autorelease 釋放時(shí)都會(huì)調(diào)用 release)
Person *p = [[[[Person alloc] init] auturelease] autorelease]
- 系統(tǒng)自帶方法里面沒(méi)有alloc、new负乡、copy牍白,說(shuō)明返回的對(duì)象是autorelease的
- 開(kāi)發(fā)中經(jīng)常會(huì)提供一些類方法,快速創(chuàng)建一個(gè)已經(jīng)autorelease過(guò)的對(duì)象
- 創(chuàng)建對(duì)象時(shí)不要直接用類名敬鬓,一般用 self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
自動(dòng)釋放池
在 IOS 程序運(yùn)行中淹朋,會(huì)創(chuàng)建無(wú)數(shù)個(gè)池子笙各。這些池子都是以“椂ご穑”結(jié)構(gòu)存在(先進(jìn)后出,“杯子”)
當(dāng)一個(gè)對(duì)象調(diào)用 autorelease 方法時(shí)杈抢,會(huì)將這個(gè)對(duì)象放到棧頂?shù)尼尫懦?(”棧頂“ 相當(dāng)于杯子底)
-
集合對(duì)象的內(nèi)存管理
- 當(dāng)把一個(gè)對(duì)象添加到集合中時(shí),這個(gè)對(duì)象會(huì)做了一次retain操作,計(jì)數(shù)器會(huì)+1
- 當(dāng)一個(gè)集合被銷毀時(shí),會(huì)對(duì)集合里面的所有對(duì)象做一次release操作,計(jì)數(shù)器會(huì)-1
- 當(dāng)一個(gè)對(duì)象從集合中移除時(shí),這個(gè)對(duì)象會(huì)一次release操作,計(jì)數(shù)器會(huì)-1
ARC
ARC 的判斷準(zhǔn)則:主要沒(méi)有強(qiáng)指針指向?qū)ο笫颍蜁?huì)釋放對(duì)象
指針的2種類型
強(qiáng)指針:_strong 默認(rèn)情況,所有指針都是強(qiáng)指針
-
弱指針:_weak
- _weak Person *p = [[Person alloc] init] 錯(cuò)誤寫法惶楼,沒(méi)有意義的寫法右蹦,一創(chuàng)建就釋放
-
ARC 特點(diǎn)
- 不允許調(diào)用 release、retain歼捐、retainCount
- 允許重寫 dealloc何陆,但是不允許調(diào)用[super dealloc]
- @property 的參數(shù)
- strong : 成員變量是強(qiáng)指針(適用于 OC 對(duì)象類型)
- weak : 成員變量是弱指針(適用于 OC 對(duì)象類型)
- assign:適用于非 OC 對(duì)象
循環(huán)引用
一端用:strong,另一端用:weak
在Compiler Flags一列加上-fno-objc-arc就表示禁止這個(gè).m文件的ARC
mrc 可以轉(zhuǎn) arc 豹储,系統(tǒng)轉(zhuǎn)換
19. copy
- copy的基本原則
- 因?yàn)榭截愐笮薷脑瓉?lái)的對(duì)象不能影響到拷貝出來(lái)得對(duì)象
- 修改拷貝出來(lái)的對(duì)象也不能影響到原來(lái)的對(duì)象, 所以需要生成一個(gè)新的對(duì)象
互不影響
- copy的使用
- 實(shí)現(xiàn)拷貝的方法有2個(gè)
- copy:返回不可變副本
- mutableCopy:返回可變副本
- 普通對(duì)象實(shí)現(xiàn)拷貝的步驟
- 遵守NSCopying協(xié)議
- 實(shí)現(xiàn)-copyWithZone:方法
- 創(chuàng)建新對(duì)象
- 給新對(duì)象的屬性賦值
20. Block
- block訪問(wèn)外面變量
- block內(nèi)部可以訪問(wèn)外面的變量
- 默認(rèn)情況下贷盲,block內(nèi)部不能修改外面的局部變量
- 給局部變量加上__block關(guān)鍵字,這個(gè)局部變量就可以在block內(nèi)部修改
- 利用typedef定義block類型
typedef int (^MyBlock)(int, int);
// 以后就可以利用MyBlock這種類型來(lái)定義block變量
- block是存儲(chǔ)在堆中還是棧中
- 默認(rèn)情況下block存儲(chǔ)在棧中, 如果對(duì)block進(jìn)行一個(gè)copy操作, block會(huì)轉(zhuǎn)移到堆中
- 如果block在棧中, block中訪問(wèn)了外界的對(duì)象, 那么不會(huì)對(duì)對(duì)象進(jìn)行retain操作
- 但是如果block在堆中, block中訪問(wèn)了外界的對(duì)象, 那么會(huì)對(duì)外界的對(duì)象進(jìn)行一次retain
- 如果在block中訪問(wèn)了外界的對(duì)象, 一定要給對(duì)象加上__block, 只要加上了__block, 哪怕block在堆中, 也不會(huì)對(duì)外界的對(duì)象進(jìn)行retain
- 如果是在ARC開(kāi)發(fā)中就需要在前面加上__weak
Person *p = [Person new];
__weak Person *weakP = p;
void (^myBlock) () = ^{
weakP.age = 10;
};
myBlock();
NSLog(@"age = %li", p.age);
21. Protocol
協(xié)議
@protocol 協(xié)議名稱 < NSObeject >
// 方法聲明列表....
@end
如何遵守協(xié)議
類遵守協(xié)議
@interface 類名 : 父類名 <協(xié)議名稱1, 協(xié)議名稱2>
@end
協(xié)議遵守協(xié)議
@protocol 協(xié)議名稱 <其他協(xié)議名稱1, 其他協(xié)議名稱2>
@end
協(xié)議中方法聲明的關(guān)鍵字
@required (默認(rèn)): 要求實(shí)現(xiàn)剥扣,如果沒(méi)有實(shí)現(xiàn)巩剖,會(huì)發(fā)出警告
@optional: 不要求實(shí)現(xiàn),不會(huì)有警告
定義一個(gè)變量的時(shí)候钠怯,限制這個(gè)變量保存的對(duì)象遵守某個(gè)協(xié)議
-
如果沒(méi)有遵守對(duì)應(yīng)的協(xié)議佳魔,編譯器會(huì)警告
類名<協(xié)議名稱> *變量名;
id<協(xié)議名稱> 變量名;
NSObject<MyProtocol> *obj;
id<MyProtocol> obj2;
-
@property中聲明的屬性也可用做一個(gè)遵守協(xié)議的限制
@property (nonatomic, strong) 類名<協(xié)議名稱> *屬性名;
@property (nonatomic, strong) id<協(xié)議名稱> 屬性名;
@property (nonatomic, strong) Dog<MyProtocol> *dog;
@property (nonatomic, strong) id<MyProtocol> dog2;
協(xié)議可用定義在單獨(dú).h文件中,也可用定義在某個(gè)類中
如果這個(gè)協(xié)議只用在某個(gè)類中晦炊,應(yīng)該把協(xié)議定義在該類中
如果這個(gè)協(xié)議用在很多類中鞠鲜,就應(yīng)該定義在單獨(dú)文件中
分類可用定義在單獨(dú).h和.m文件中宁脊,也可用定義在原來(lái)類中
一般情況下,都是定義在單獨(dú)文件
定義在原來(lái)類中的分類镊尺,只要求能看懂語(yǔ)法
- 動(dòng)態(tài)類型能使程序直到執(zhí)行時(shí)才確定對(duì)象的真實(shí)類型
- 動(dòng)態(tài)類型綁定能使程序直到執(zhí)行時(shí)才確定要對(duì)那個(gè)對(duì)象調(diào)用的方法
假設(shè) 子類 Dog 有一個(gè)特有的方法bark
[dog bark];
Animal *an = [Dog new];
[(Dog*)an bark]; //把父類的指針,強(qiáng)制類型轉(zhuǎn)換
點(diǎn)語(yǔ)法使用注意
p.age = 10; // [p setAge:10]
self.age = age; // [self setAge:age]
在@implementation 中定義的私有變量只能在本類中查看
Property 編譯器指令
生成setter 和 getter 方法聲明(未加強(qiáng)版)
-(void)setAge:(int)age;
-(int)age;
@property int age;
@synthesize age = _age;
setter和getter實(shí)現(xiàn)中會(huì)訪問(wèn)成員變量_age, 如果成員變量_age不存在为流,就會(huì)自動(dòng)生成一個(gè)@private的成員變量_age
@synthesize age;
setter和getter實(shí)現(xiàn)中會(huì)訪問(wèn)@synthesize后同名成員變量age
如果成員變量age不存在,就會(huì)自動(dòng)生成一個(gè)@private的成員變量age
多個(gè)屬性可以通過(guò)一行@synthesize搞定,多個(gè)屬性之間用逗號(hào)連接
@synthesize age = _age, number = _number, name = _name;
Property 增強(qiáng)
只要利用一個(gè)@property就可以同時(shí)生成setter/getter方法的聲明和實(shí)現(xiàn)傳入的屬性賦值給_開(kāi)頭的成員變量
@property有一個(gè)弊端: 它只會(huì)生成最簡(jiǎn)單的getter/setter方法的聲明和實(shí)現(xiàn), 并不會(huì)對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾
如果想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 那么我們就必須重寫getter/setter方法如果不想對(duì)傳入的數(shù)據(jù)進(jìn)行過(guò)濾, 僅僅是提供一個(gè)方法給外界操作成員變量, 那么就可以使用@property
注意: 如果沒(méi)有會(huì)自動(dòng)生成一個(gè)_開(kāi)頭的成員變量,自動(dòng)生成的成員變量是私有變量, 聲明在.m中,在其它文件中無(wú)法查看,但當(dāng)可以在本類中查看
有就不生成让簿,沒(méi)有就生成
- 如果重寫了setter方法, 那么property就只會(huì)生成getter方法
- 如果重寫了getter方法, 那么property就只會(huì)生成setter方法
- 如果同時(shí)重寫了getter/setter方法, 那么property就不會(huì)自動(dòng)幫我們生成私有的成員變量
@property(屬性修飾符) 數(shù)據(jù)類型 變量名稱;
readwrite:代表生成 getter 和 setter 方法敬察,默認(rèn)就是
readonly:代表只生成 getter 方法,(只讀)
修改 getter 方法名(常用)
程序員之間有一個(gè)約定, 一般情況下獲取BOOL類型的屬性的值, 我們都會(huì)將獲取的方法名稱改為isXXX
Person *p = [Person alloc] init]]
id p = [[Person alloc] init];
- 通過(guò)靜態(tài)數(shù)據(jù)類型定義變量, 不能調(diào)用子類特有的方法
- 通過(guò)動(dòng)態(tài)數(shù)據(jù)類型定義變量, 可以調(diào)用子類特有的方法
- 通過(guò)動(dòng)態(tài)數(shù)據(jù)類型定義的變量, 可以調(diào)用私有方法
id obj = [Student new];
[obj isKindOfClass:[Student class]]
//isKindOfClass , 判斷指定的對(duì)象是否是某一個(gè)類, 或者是某一個(gè)類的子類
用來(lái)初始化對(duì)象的方法.
重寫構(gòu)造方法的注意:
先調(diào)用父類的構(gòu)造方法.
再進(jìn)行子類內(nèi)部的成員變量的初始化
返回當(dāng)前對(duì)象的地址
-(instancetype)init
{
// 注意: 不要把 = 號(hào)寫為 ==
// 一定要將[super init]的返回值賦值給self
if (self = [super init]) {
// 初始化子類
_age = 6;
}
return self;
}
自定義構(gòu)造方法
自己做自己的事情
父類的屬性交給父類的方法來(lái)處理,子類的方法處理子類自己獨(dú)有的屬性
自定義構(gòu)造方法必須以intiWith開(kāi)頭,并且’W’必須大寫
類工廠方法:
用于快速創(chuàng)建對(duì)象的類方法, 我們稱之為類工廠方法
類工廠方法中主要用于 給對(duì)象分配存儲(chǔ)空間和初始化這塊存儲(chǔ)空間
規(guī)范:
- 一定是類方法 +
- 方法名稱以類的名稱開(kāi)頭, 首字母小寫
- 一定有返回值, 返回值是id/instancetype
- 注意: 以后但凡自定義類工廠方法, 在類工廠方法中創(chuàng)建對(duì)象一定不要使用類名來(lái)創(chuàng)建莲祸,
一定要使用self來(lái)創(chuàng)建
return [[self alloc] init];
訪問(wèn)原始類的成員變量
, 但不能添加變量, 只能添加方法. 如果想 添加變量, 可以考慮通過(guò)繼承創(chuàng) 建子類 @interface 類名 ()
@end
父類 → 子類→ 分類
锐帜。不管程序運(yùn)行過(guò)程有沒(méi)有用到這個(gè)類, 都會(huì)調(diào)用 + load 方法。```
Class c = [Person class] // 類方法
Person *p = [Person new];
Class c1 = [p class]; // 對(duì)象方法
```
- 實(shí)例對(duì)象 → 類對(duì)象(對(duì)象方法)→ 元類對(duì)象(類方法)→ 根元類 (isa 指向自己)
如下圖 :
- description方法是基類NSObject 所帶的方法. 使用NSLog輸出OC對(duì)象,意義就不是很大,因?yàn)槲覀儾⒉魂P(guān)心對(duì)象的內(nèi)存地址,比較關(guān)心的是對(duì)象內(nèi)部的一些成變量的值视卢。因此,會(huì)經(jīng)常重寫description方法,覆蓋description方法 的默認(rèn)實(shí)現(xiàn)
-(NSString *) description { return [NSString stringWithFormat:@"age = %d", _age] }
不常用
)代表方法的簽名踱卵,在類對(duì)象的方法列表
中存儲(chǔ)著該簽名與方法代碼的對(duì)應(yīng)關(guān)系
每個(gè)方法都有一個(gè)與之對(duì)應(yīng)的 SEL類型的對(duì)象
- SEL 其實(shí)是對(duì)方法的一種包裝, 將方法包裝成一個(gè) SEL 類型的數(shù)據(jù), 去找對(duì)應(yīng)的方法地址, 進(jìn)而進(jìn)行調(diào)用
- 注意:在這個(gè)操作過(guò)程中又緩存,第一次找的時(shí)候一個(gè)一個(gè)的找据过,非常耗性能惋砂,之后再用到的時(shí)候就直接使用
對(duì)象是否實(shí)現(xiàn)了某個(gè)方法
- - (BOOL) respondsToSelector: (SEL)selector 判斷實(shí)例是否實(shí)現(xiàn)這樣方法
- + (BOOL)instancesRespondToSelector:(SEL)aSelector; (類對(duì)象)
讓對(duì)象執(zhí)行某個(gè)方法
- (id)performSelector:(SEL)aSelector;
SEL 類型的定義 typedef struct objc_selector *SEL
//SEL 對(duì)象的創(chuàng)建
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test”);
// 將SEL對(duì)象轉(zhuǎn)為NSString對(duì)象
NSString *str = NSStringFromSelector(@selector(test));
Person *p = [Person new];
// 每個(gè)類都有以個(gè)_cmd 代表當(dāng)前方法
// 調(diào)用對(duì)象p的test方法
[p performSelector : @selector (test)];
- 每個(gè)OC對(duì)象都有自己的引用計(jì)數(shù)器绳锅,它是一個(gè)整數(shù)西饵,從字面上, 可以理解為”對(duì)象被引用的次數(shù)”
- 也可以理解為: 它表示有多少人正在用這個(gè)對(duì)象
占4個(gè)字節(jié)
什么是手動(dòng)引用計(jì)數(shù)?
- 所有對(duì)象的內(nèi)容都需要我們手動(dòng)管理, 需要程序員自己編寫release/retain等代碼
方法的基本使用
- retain : 計(jì)數(shù)器 +1 , 會(huì)返回對(duì)象本身
- release : 計(jì)數(shù)器 -1 , 沒(méi)有返回值(
release并不代表銷毀對(duì)象, 僅僅是計(jì)數(shù)器-1
) - retainCount : 獲取當(dāng)前的計(jì)數(shù)器
概念
- 僵尸對(duì)象: 所占用內(nèi)存已經(jīng)被回收的對(duì)象, 僵尸對(duì)象不能再使用
- 野指針: 指向僵尸對(duì)象(不可用內(nèi)存)的指針, 給野指針發(fā)送消息會(huì)報(bào)錯(cuò)(EXC_BAD_ACCES)
- 空指針: 沒(méi)有指向任何東西的指針(儲(chǔ)存的東西是nil NULL 0), 給空指針發(fā)送消息不會(huì)報(bào)錯(cuò)
- 基本數(shù)據(jù)類型: 直接復(fù)制
-(void)setAge:(int)age
{
_age = age;
}
```
- OC對(duì)象類型
```
-(void)setCar:(Car *)car
{
// 先判斷是不是新傳進(jìn)來(lái)對(duì)象
if( car != _car)
{
// 對(duì)舊對(duì)象做一次release
[_car release]
// 對(duì)新對(duì)象做一次retain
_car = [car retain]
}
}
```
- 對(duì)self(當(dāng)前)所擁有的其他對(duì)象做一次release
- 當(dāng)一個(gè)對(duì)象要被回收的時(shí)候, 就會(huì)調(diào)用
- 一定要調(diào)用
[super dealloc]
, 這句調(diào)用放在最后面
- retain : release 舊值 , retain 新值 (適用于OC對(duì)象類型)
- assign : 直接賦值(默認(rèn), 適用于非OC對(duì)象類型)
- copy : release 舊值, copy 新值
- readwrite : 同時(shí)生成setter 和 getter的聲明, 實(shí)現(xiàn)(默認(rèn))
- readonly : 只會(huì)生成getter的聲明, 實(shí)現(xiàn)
- nonatomic : 性能高 (一般就用這個(gè))
- atomic : 性能低(默認(rèn))
- setter : 決定了set方法的名稱, 一定要有個(gè)冒號(hào) :
- getter : 決定了get方法的名稱(一般用在BOOL類型)
僅僅告訴編譯器,某個(gè)名稱是一個(gè)類
開(kāi)發(fā)中引用一個(gè)類的規(guī)范
- 在.h 文件中用@class 來(lái)聲明類
- 在.m 文件中用#import 來(lái)包含類的所有東西
和#import 的區(qū)別(面試)
- import會(huì)包含引用類的所有信息(內(nèi)容),包括引用類的變量和方法
- @class僅僅是告訴編譯器有這么一個(gè)類, 具體這個(gè)類里有什么信息, 完全不知
總結(jié):
- 如果都在.h中import, 假如A拷貝了B, B拷貝了C , 如果C被修改了, 那么B和A都需要重新拷貝. 因?yàn)镃修改了那么B就會(huì)重新拷貝, 而B重新拷貝之后相當(dāng)于B也被修改了, 那么A也需要重新拷貝. 也就是說(shuō)如果都在.h中拷貝, 只要有間接關(guān)系都會(huì)重新拷貝
- 如果在.h中用@class, 在.m中用import, 那么如果一個(gè)文件發(fā)生了變化, 只有和這個(gè)文件有直接關(guān)系的那個(gè)文件才會(huì)重新拷貝
- 所以在.h中用@class可以提升編譯效率
兩端循環(huán)引用(面試)
retain
* 比如A對(duì)象retain了B對(duì)象鳞芙,B對(duì)象retain了A對(duì)象眷柔,這樣會(huì)導(dǎo)致A對(duì)象和B對(duì)象永遠(yuǎn)無(wú)法釋放期虾。
* 當(dāng)兩端互相引用時(shí),應(yīng)該一端用retain驯嘱、一端用assign镶苞。
import
* 如果兩個(gè)類相互(#import<>
)拷貝, 例如A拷貝B, B拷貝A, 這樣會(huì)報(bào)錯(cuò)
- 如何解決: 在.h中用@class, 在.m中用import
- 因?yàn)槿绻?h中都用import, 那么A拷貝B, B又拷貝A, 會(huì)形成死循環(huán)
- 如果在.h中用@class, 那么不會(huì)做任何拷貝操作, 而在.m中用import只會(huì)拷貝對(duì)應(yīng)的文件, 并不會(huì)形成死循環(huán)
- 不用關(guān)心對(duì)象釋放的時(shí)間
- 不用關(guān)心什么時(shí)候調(diào)用 release
- 占用內(nèi)存較大的對(duì)象不要隨便用 autorelease
- 占用內(nèi)存較小的對(duì)象使用 autorelease,沒(méi)有太大影響(影響:不能控制對(duì)象的釋放時(shí)間)
- alloc 之后調(diào)用了 autorelease 剃幌,又調(diào)用 release
Person *p = [[[Person alloc] init] autorelease];
[p release]; - 連續(xù)調(diào)用autorelease(野指針錯(cuò)誤聋涨,每個(gè)autorelease 釋放時(shí)都會(huì)調(diào)用 release)
Person *p = [[[[Person alloc] init] auturelease] autorelease]
- 開(kāi)發(fā)中經(jīng)常會(huì)提供一些類方法,快速創(chuàng)建一個(gè)已經(jīng)autorelease過(guò)的對(duì)象
- 創(chuàng)建對(duì)象時(shí)不要直接用類名敬鬓,一般用 self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
自動(dòng)釋放池
在 IOS 程序運(yùn)行中淹朋,會(huì)創(chuàng)建無(wú)數(shù)個(gè)池子笙各。這些池子都是以“椂ご穑”結(jié)構(gòu)存在(先進(jìn)后出,“杯子”)
當(dāng)一個(gè)對(duì)象調(diào)用 autorelease 方法時(shí)杈抢,會(huì)將這個(gè)對(duì)象放到棧頂?shù)尼尫懦?(”棧頂“ 相當(dāng)于杯子底)
集合對(duì)象的內(nèi)存管理
- 當(dāng)把一個(gè)對(duì)象添加到集合中時(shí),這個(gè)對(duì)象會(huì)做了一次retain操作,計(jì)數(shù)器會(huì)+1
- 當(dāng)一個(gè)集合被銷毀時(shí),會(huì)對(duì)集合里面的所有對(duì)象做一次release操作,計(jì)數(shù)器會(huì)-1
- 當(dāng)一個(gè)對(duì)象從集合中移除時(shí),這個(gè)對(duì)象會(huì)一次release操作,計(jì)數(shù)器會(huì)-1
ARC 的判斷準(zhǔn)則:主要沒(méi)有強(qiáng)指針指向?qū)ο笫颍蜁?huì)釋放對(duì)象
指針的2種類型
強(qiáng)指針:_strong 默認(rèn)情況,所有指針都是強(qiáng)指針
弱指針:_weak
- _weak Person *p = [[Person alloc] init] 錯(cuò)誤寫法惶楼,沒(méi)有意義的寫法右蹦,一創(chuàng)建就釋放
ARC 特點(diǎn)
- 不允許調(diào)用 release、retain歼捐、retainCount
- 允許重寫 dealloc何陆,但是不允許調(diào)用[super dealloc]
- @property 的參數(shù)
- strong : 成員變量是強(qiáng)指針(適用于 OC 對(duì)象類型)
- weak : 成員變量是弱指針(適用于 OC 對(duì)象類型)
- assign:適用于非 OC 對(duì)象
循環(huán)引用
一端用:strong,另一端用:weak
在Compiler Flags一列加上-fno-objc-arc就表示禁止這個(gè).m文件的ARC
mrc 可以轉(zhuǎn) arc 豹储,系統(tǒng)轉(zhuǎn)換
- 因?yàn)榭截愐笮薷脑瓉?lái)的對(duì)象不能影響到拷貝出來(lái)得對(duì)象
- 修改拷貝出來(lái)的對(duì)象也不能影響到原來(lái)的對(duì)象, 所以需要生成一個(gè)新的對(duì)象
互不影響
- 實(shí)現(xiàn)拷貝的方法有2個(gè)
- copy:返回不可變副本
- mutableCopy:返回可變副本
- 遵守NSCopying協(xié)議
- 實(shí)現(xiàn)-copyWithZone:方法
- 創(chuàng)建新對(duì)象
- 給新對(duì)象的屬性賦值
- block內(nèi)部可以訪問(wèn)外面的變量
- 默認(rèn)情況下贷盲,block內(nèi)部不能修改外面的局部變量
- 給局部變量加上__block關(guān)鍵字,這個(gè)局部變量就可以在block內(nèi)部修改
typedef int (^MyBlock)(int, int);
// 以后就可以利用MyBlock這種類型來(lái)定義block變量
- 默認(rèn)情況下block存儲(chǔ)在棧中, 如果對(duì)block進(jìn)行一個(gè)copy操作, block會(huì)轉(zhuǎn)移到堆中
- 如果block在棧中, block中訪問(wèn)了外界的對(duì)象, 那么不會(huì)對(duì)對(duì)象進(jìn)行retain操作
- 但是如果block在堆中, block中訪問(wèn)了外界的對(duì)象, 那么會(huì)對(duì)外界的對(duì)象進(jìn)行一次retain
- 如果在block中訪問(wèn)了外界的對(duì)象, 一定要給對(duì)象加上__block, 只要加上了__block, 哪怕block在堆中, 也不會(huì)對(duì)外界的對(duì)象進(jìn)行retain
- 如果是在ARC開(kāi)發(fā)中就需要在前面加上__weak
Person *p = [Person new];
__weak Person *weakP = p;
void (^myBlock) () = ^{
weakP.age = 10;
};
myBlock();
NSLog(@"age = %li", p.age);
協(xié)議
@protocol 協(xié)議名稱 < NSObeject >
// 方法聲明列表....
@end
如何遵守協(xié)議
類遵守協(xié)議
@interface 類名 : 父類名 <協(xié)議名稱1, 協(xié)議名稱2>
@end
協(xié)議遵守協(xié)議
@protocol 協(xié)議名稱 <其他協(xié)議名稱1, 其他協(xié)議名稱2>
@end
協(xié)議中方法聲明的關(guān)鍵字
@required (默認(rèn)): 要求實(shí)現(xiàn)剥扣,如果沒(méi)有實(shí)現(xiàn)巩剖,會(huì)發(fā)出警告
@optional: 不要求實(shí)現(xiàn),不會(huì)有警告
定義一個(gè)變量的時(shí)候钠怯,限制這個(gè)變量保存的對(duì)象遵守某個(gè)協(xié)議
如果沒(méi)有遵守對(duì)應(yīng)的協(xié)議佳魔,編譯器會(huì)警告
類名<協(xié)議名稱> *變量名;
id<協(xié)議名稱> 變量名;
NSObject<MyProtocol> *obj;
id<MyProtocol> obj2;
@property中聲明的屬性也可用做一個(gè)遵守協(xié)議的限制
@property (nonatomic, strong) 類名<協(xié)議名稱> *屬性名;
@property (nonatomic, strong) id<協(xié)議名稱> 屬性名;
@property (nonatomic, strong) Dog<MyProtocol> *dog;
@property (nonatomic, strong) id<MyProtocol> dog2;
協(xié)議可用定義在單獨(dú).h文件中,也可用定義在某個(gè)類中
如果這個(gè)協(xié)議只用在某個(gè)類中晦炊,應(yīng)該把協(xié)議定義在該類中
如果這個(gè)協(xié)議用在很多類中鞠鲜,就應(yīng)該定義在單獨(dú)文件中
分類可用定義在單獨(dú).h和.m文件中宁脊,也可用定義在原來(lái)類中
一般情況下,都是定義在單獨(dú)文件
定義在原來(lái)類中的分類镊尺,只要求能看懂語(yǔ)法