分類
用分類做了哪些事情
- 聲明私有方法
- 分解體積龐大的類文件
- 把framework的私有方法公開化
特點
- 運行時決議:編好分類文件,并沒有分類中添加的內(nèi)容附加到相應(yīng)的宿主類批糟,而是在運行時通過RunTime真實的添加到相應(yīng)的宿主類
- 可以為系統(tǒng)類添加分類
分類中都可以添加哪些內(nèi)容凶异?
- 實例方法
- 類方法
- 協(xié)議
- 屬性(分類中定義屬性蜀撑,實際上只是生成了對應(yīng)的getter、setter方法)
Runtime的源碼分類的結(jié)構(gòu)體:struct _category_t
分類的調(diào)用棧(Category)
- _objc_int -> map_2_images -> map_images_nolock -> _read_images -> remethodizeClass
- images是鏡像
- _read_images 加載一些可執(zhí)行文件到內(nèi)存當中進行處理
- remethodizeClass
如果兩個分類添加同一個函數(shù)名稱的函數(shù)剩彬,哪一個函數(shù)最終生效酷麦?
取決于編譯順序,最后生成分類的函數(shù)會生效喉恋,前面的將被覆蓋掉沃饶。
總結(jié):
分類添加的方法可以”覆蓋”原有方法
① 實際上是runtime在查找這個類的方法時,會查找分類一個宿主類的所有方法所在數(shù)組
② 而這個數(shù)組的分類方法是在宿主類前面的轻黑,所以說分類方法的調(diào)用有優(yōu)先宿主類
③ 而并不是覆蓋宿主類的方法糊肤。同名分類方法誰能生效取決于編譯順序
最后被編譯的分類,會優(yōu)先生效名字相同的分類會引起編譯報錯
生成具體分類苔悦,會把添加的分類名稱轩褐,以下劃線的形式拼接到宿主類中,這樣在添加過程中玖详,如果名稱一樣會報錯把介。
能否給分類添加”成員變量”勤讽?
不能在分類的聲明和定義實現(xiàn)的時候直接為分類添加成員變量,但是可以通過關(guān)聯(lián)對象的技術(shù)來為分類添加”成員變量”拗踢,來達到分類可以添加”成員變量的效果”脚牍。
關(guān)聯(lián)對象的3個API:
objc_setAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>, <#id _Nullable value#>, <#objc_AssociationPolicy policy#>)
objc_getAssociatedObject(<#id _Nonnull object#>, <#const void * _Nonnull key#>)
objc_removeAssociatedObjects(<#id _Nonnull object#>)
① 設(shè)定一個value值,通過key值建立一個映射關(guān)系巢墅,然后通過policy策略诸狭,關(guān)聯(lián)到object上
② 根據(jù)指定的key到object中獲取關(guān)聯(lián)的值
③ 根據(jù)對象,移除所有相關(guān)它的對象
我們用關(guān)聯(lián)對象技術(shù)來實現(xiàn)為分類添加”成員變量”君纫,這個成員變量會被添加到哪里驯遇?
① 我們?yōu)榉诸愃砑拥某蓡T變量肯定不是在宿主類中的。
② 關(guān)聯(lián)對象由AssociationsManager管理并在AssociationHashMap存儲蓄髓。
③ 我們創(chuàng)建的每一個對象的關(guān)聯(lián)對象實際上都存儲在了AssociationHashMap這樣的容器中叉庐。
④ 所有對象的關(guān)聯(lián)內(nèi)容都在同一個全局容器中
⑤ 不同對象的關(guān)聯(lián)對象的值都會放到全局容器中
⑥ 假如想消除一個關(guān)聯(lián)對象時,可以傳入value為nil操作來實現(xiàn)
關(guān)聯(lián)對象
關(guān)聯(lián)對象本質(zhì)
關(guān)聯(lián)對象由AssociationsManager管理并在AssociationHashMap存儲会喝。
我們創(chuàng)建的每一個對象的關(guān)聯(lián)對象實際上都存儲在了AssociationHashMap這樣的容器中陡叠。
所有對象的關(guān)聯(lián)內(nèi)容都在同一個全局容器中
利用關(guān)聯(lián)對象添加成員變量
1. - (void)setName:(NSString *)name { objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
2. - (NSString *)name
{
// 隱式參數(shù)
// _cmd == @selector(name)
return objc_getAssociatedObject(self, _cmd);
}
(void)removeAssociatedObjects{
// 移除所有關(guān)聯(lián)對象
objc_removeAssociatedObjects(self);
}
擴展
類擴展一般都在宿主類的.m文件中。
一般用擴展做什么肢执?
- 聲明私有屬性
- 聲明私有方法(沒有多大作用枉阵,方便閱讀)
- 聲明私有成員變量
聲明私有屬性、聲明私有成員變量是有區(qū)別的
特點
- 分類是運行時決議预茄、擴展是編譯時決議
- 分類可以有聲明和實現(xiàn)兴溜,而擴展只有聲明的形式,實現(xiàn)是直接在宿主類的
- 可以為系統(tǒng)類添加分類反璃,但是不能為系統(tǒng)類添加擴展
代理
- 準確的說是一種軟件設(shè)計模式
- iOS 當中以@protocol形式體現(xiàn)
- 傳遞方式一對一
代理的工作流程
三個角色:
- 委托方
① 要求代理方需要實現(xiàn)的接口昵慌,也就是協(xié)議
② 調(diào)用代理方遵從協(xié)議的方法
③ 可能返回一個處理結(jié)果 - 協(xié)議
① 可以定義方法(必須實現(xiàn)和可選類型)
② 可以定義屬性 -
代理方
按照協(xié)議實現(xiàn)方法
代理方和委托方以什么關(guān)系存在?
一般聲明為weak來規(guī)避循環(huán)引用
通知實現(xiàn)機制
- 使用觀察者模式淮蜈,來實現(xiàn)用于跨層傳遞消息的機制
- 傳遞方式一對多
如何實現(xiàn)通知的機制斋攀?假如說自己實現(xiàn)這種機制如何實現(xiàn)?
- 實現(xiàn)一個實現(xiàn)一個全局Map
- key為notificationName
- value為一個Serrvers(因為一對多)梧田, servers中的每個元素應(yīng)該能明確哪一個對象淳蔼,哪一個方法
通知傳遞消息一對多的流程
通知和代理的區(qū)別
- 代理使用代理模式,而通知是由觀察者模式實現(xiàn)的
- 代理是一對一裁眯,而通知是一對多
KVO
什么是KVO鹉梨?
- KVO是key-value observing的縮寫
- KVO是Objective-C對觀察者設(shè)計模式的又一實現(xiàn)
- KVO是Objective-C對觀察者設(shè)計模式的又一實現(xiàn)
KVO的實現(xiàn)機制
- 系統(tǒng)調(diào)用addObserver添加KVO的時候
- 會想被觀察者對象的isa指向其名為NSKVONotifying_<ObjectClass>子類
- NSKVONotifying_<ObjectClass>中重寫了setter方法
- 重寫的setter方法負責通知觀察者
重寫setter方法
- -(void)willChangeValueForKey:(NSString *)key;
- -(void)didChangeValueForKey:(NSString *)key;
通過KVC設(shè)置value能夠生效?
- 通過KVC設(shè)置value穿稳,KVO的通知會生效
- KVC設(shè)置value會調(diào)用setter方法
通過成員變量直接賦值value能否生效
- 不會觸發(fā)系統(tǒng)的KVO的
- 不會調(diào)用setter方法
- 可以在變量賦值的代碼塊前后添加下面兩個API來觸發(fā)KVO
① -(void)willChangeValueForKey:(NSString *)key;
② -(void)didChangeValueForKey:(NSString *)key;
觀測方法的options
- NSKeyValueObservingOptionOld 把更改之前的值提供給處理方法
- NSKeyValueObservingOptionNew 把更改之后的值提供給處理方法
- NSKeyValueObservingOptionInitial 把初始化的值提供給處理方法存皂,一旦注冊,立馬就會調(diào)用一次。通常它會帶有新值旦袋,而不會帶有舊值骤菠。
- NSKeyValueObservingOptionPrior 分2次調(diào)用。在值改變之前和值改變之后疤孕。
總結(jié)
- 使用setter方法改變值KVO才會生效
- 使用setValue:forkey:改變KVO才會生效
- 成員變量直接修改需手動添加KVO才會生效
KVC
什么是KVC商乎?
- KVC是Key-value coding的縮寫
- KVC的兩個API
① -(void)setValue:(nullable id)value forKey:(NSString *)key
② -(nullable id)valueForKey:(NSString *)key
使用KVC是否會違背于面向?qū)ο蟮木幊趟枷?/h2>
KVC的key是沒有限制的,如果我們知道這個對象內(nèi)部的私有變量名稱的話祭阀,我們可以在外界修改鹉戚、訪問它,從這個角度來考慮的話是會破壞面向?qū)ο蟮乃枷氲?/p>
valueForKey的實現(xiàn)流程
Accessor Method方法的定義
- instance var
- _key
- _isKey
- key
- isKey
setValue:forKey的實現(xiàn)流程
Accessor Method方法的定義
- set:
- _set:
- setIs:
instance var
- _key
- _isKey
- key
- isKey
屬性關(guān)鍵字
屬性關(guān)鍵字分為哪幾類
- 讀寫權(quán)限
① readonly
② readwrite iOS默認 - 原子性
① atomic iOS默認
-修飾的是一個數(shù)組专控,賦值和獲取是線程安全的抹凳,
-修改這個數(shù)組中的數(shù)據(jù)不是線程安全的 (例如在atomic修飾的NSMutableArray數(shù)組中,進行增刪元素操作)
② nonatomic - 引用技計數(shù)
① retain mrc
② strong arc
③ assign 既可以修飾對象伦腐,也可以修飾值類型數(shù)據(jù)
④ unsafe_unretained MRC使用的比較多却桶,ARC基本上不怎么用
⑤ weak
⑥ copy
assign的特點
- 修飾基本數(shù)據(jù)類型,如果int蔗牡、bool等
- 修飾對象類型時,不改變引用計數(shù)
- 在對象釋放以后嗅剖,assign修飾的對象指針還是會指向這個對象地址辩越,當使用這個對象的時候,會出現(xiàn)野指針信粮。
weak 特點
- 不改變對象引用計數(shù)
- 所指對象在釋放之后會自動置為nil
assign和weak的區(qū)別有哪些黔攒?
1、weak只可以修飾對象强缘、而assign可以修飾基本數(shù)據(jù)類型和對象
2督惰、assign修飾的對象的時候,當對象被釋放掉以后旅掂,assign指針還是會指向?qū)ο蟮牡刂飞团撸鴚eak修飾的對象,在對象釋放后商虐,weak的指針會被置nil
copy
源對象類型 拷貝方式 目標對象類型 拷貝類型
mutable對象 copy 不可變 深拷貝
mutable對象 mutableCopy 可變 深拷貝
immutable對象 copy 不可變 淺拷貝
immutable對象 mutableCopy 可變 深拷貝
① 可變對象的copy和mutablecopy都是深拷貝
② 不可變對象的copy是淺拷貝觉阅,mutablecopy是深拷貝
③ copy方法返回的都是不可變對象
淺拷貝
- 淺拷貝會增加被拷貝對象的引用計數(shù)
-
并沒有發(fā)生新的內(nèi)存分配
淺拷貝就是對內(nèi)存地址的復(fù)制,讓目標對象指針和源對象指向同一片內(nèi)存空間秘车。
深拷貝
- 不會增加被拷貝對象的引用計數(shù)
-
產(chǎn)生了內(nèi)存分配
深拷貝讓目標對象指針和源對象指針指向兩片內(nèi)容相同的內(nèi)存空間
深拷貝VS淺拷貝典勇?
- 是否開辟新的內(nèi)存空間
- 是否影響引用計數(shù)
@property(copy) NSMutableArray *array, 會產(chǎn)生什么樣的問題
最終結(jié)果是一個不可變的對象,那么你在修改這個數(shù)組的時候會崩潰
- 如果賦值過來的是NSMutableArray叮趴,copy之后雖然是深拷貝割笙,但是返回的是NSArray
- 如果賦值過來的是NSArray,copy之后是NSArray
- 當修改array這個數(shù)組的時候就會出現(xiàn)崩潰問題
MST
1眯亦、MRC下如何重寫retain修飾變量的setter方法伤溉?
@property(nonatomic,retain)id obj;
- (void)setObj:(id)obj{
//對象不等判斷
//如果傳遞過來的obj對象是原來的obj對象
//實際上也是在傳遞過來的obj對象release的操作般码,有可能會直接釋放掉了傳遞過來的對象,
//這時候如果還去訪問被釋放的對象就會崩潰
if(_obj != obj){
[_obj release];
_obj = [obj retain]
}
}
2谈火、 分類的實現(xiàn)原理
- 是由運行時來決議的
- 不同分類侈询,同名函數(shù)名稱,誰最后生效糯耍,取決于扔字,誰最后參與編譯的同名分類方法會生效
- 如果分類方法的名字和宿主的方法名稱一樣,那么分類的方法會覆蓋宿主類的方法温技,這里的覆蓋是runtime優(yōu)先處理數(shù)組靠前的方法革为,如果找到就調(diào)用。宿主類的同名方法還是存在的
3舵鳞、KVO實現(xiàn)原理是怎么樣的震檩?
- KVO是系統(tǒng)基于觀察模式的一個實現(xiàn)
- KVO通過isa的混寫技術(shù)來動態(tài)運行時去為某一個類添加子類,并重寫setter方法蜓堕,同時把原有類的isa指針指向這個子類
4抛虏、能否為分類添加實例變量?
- 實例變量(成員變量)套才,即它只能添加屬性迂猴,setter/getter的聲明,但是沒有實現(xiàn)背伴,不能使用其父類的一些成員方法
- 屬性是系統(tǒng)自動為我們生成的一個添加下劃線的實例變量沸毁,系統(tǒng)幫我們實現(xiàn)了setter/getter方法
- 不使用屬性創(chuàng)建的實例變量,需要最后在@implementation中用synthesize生成set方法
@synthesize name;(創(chuàng)建實例變量的setter傻寂、getter方法)息尺,@dynamic告訴編譯器,不自動生成getter/setter方法疾掰, - 為分類添加實例變量
① 使用關(guān)聯(lián)對象技術(shù)為分類添加“實例變量”(只是達到一個成員變量的效果搂誉,實際上并不是真正的添加了實例變量)
② 代碼實例
.h文件
@interface CustomView (dd)
- (NSString *)name;
- (void)setName:(NSString *)name;
@end
.m
@implementation CustomView (dd)
/*
* 使用關(guān)聯(lián)對象模擬實例變量
* 使用objc_getAssociatedObject、objc_setAssociatedObject模擬『屬性』的存取方法
*/
- (NSString *)name{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end