OC語(yǔ)言相關(guān)
分類Category
-
使用場(chǎng)景
- 可以減少單個(gè)類的體積豹缀,降低耦合性缓溅,同一個(gè)類可以多人進(jìn)行開發(fā)
- 可以為系統(tǒng)類添加分類進(jìn)行拓展
- 模擬多繼承
- 把靜態(tài)庫(kù)的私有方法公開
-
特點(diǎn)
- 分類可以添加屬性剂习,但是并不會(huì)自動(dòng)生成成員變量及set/get方法玫恳。
因?yàn)閏ategory_t結(jié)構(gòu)體中并不存在成員變量哆姻。
成員變量是存放在實(shí)例對(duì)象中的焦影,并且編譯的那一刻就已經(jīng)決定好了车遂。
而分類是在運(yùn)行時(shí)才去加載的。那么我們就無(wú)法再程序運(yùn)行時(shí)將分類的成員變量中添加到實(shí)例對(duì)象的結(jié)構(gòu)體中斯辰。因此分類中不可以添加成員變量舶担。 - 如果分類中有和原有類同名的方法, 會(huì)優(yōu)先調(diào)用分類中的方法, 就是說(shuō)會(huì)忽略原有類的方法。
- 如果多個(gè)分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時(shí)候執(zhí)行誰(shuí)由編譯器決定彬呻,編譯器會(huì)執(zhí)行最后一個(gè)參與編譯的分類中的方法衣陶。
- 分類可以添加屬性剂习,但是并不會(huì)自動(dòng)生成成員變量及set/get方法玫恳。
為什么分類中不能增加實(shí)例變量?
category本身是個(gè)結(jié)構(gòu)體闸氮,而結(jié)構(gòu)體沒有設(shè)計(jì)實(shí)例變量祖搓,對(duì)應(yīng)也就沒有用于存儲(chǔ)的內(nèi)存空間,無(wú)法在類內(nèi)部進(jìn)行存儲(chǔ)湖苞。
@property 本身就是用于兩個(gè)方法(setter、getter)详囤,當(dāng)實(shí)例變量沒地方存儲(chǔ)時(shí)财骨,對(duì)應(yīng)也就取不到值-
為什么覆蓋原方法
- Category是在runtime時(shí)候加載,而不是在編譯的時(shí)候
- 結(jié)構(gòu)體中有存儲(chǔ)實(shí)例方法的列表藏姐、存儲(chǔ)類方法的列表隆箩、存儲(chǔ)協(xié)議的和屬性的列表
- 方法的調(diào)用就是消息的發(fā)送,如果調(diào)用實(shí)例方法羔杨,就是通過isa去類對(duì)象中查找對(duì)應(yīng)的方法捌臊,如果調(diào)用類方法、就是通過isa去元類對(duì)象中查找對(duì)應(yīng)的方法兜材。
- 當(dāng)分類理澎、原來(lái)類逞力、原來(lái)類的父類中有相同方法時(shí),方法調(diào)用的優(yōu)先級(jí):分類(最后參與編譯的分類優(yōu)先) –> 原來(lái)類 –> 父類糠爬,即先去調(diào)用分類中的方法寇荧,分類中沒這個(gè)方法再去原來(lái)類中找,原來(lái)類中沒有再去父類中找执隧。
-
多個(gè)分類中有同名方法如何生效揩抡?
- 這個(gè)是與編譯順序有關(guān),最后編譯的分類中對(duì)應(yīng)的信息會(huì)在整合在類或元類對(duì)應(yīng)列表的最前邊镀琉。所以是調(diào)用最后編譯的分類中的方法峦嗤!可以查看Build Phases ->Complie Source 中的編譯順序!
分類的加載流程屋摔?
1)runtime初始化入口函數(shù)(objc-os.mm)
2)objc-runtime-new.mm中的map_images函數(shù)
3)_read_images函數(shù)內(nèi)部通過dyld搜索Category并加載分類
4) 來(lái)自objc-os.mm文件的 _dyld_objc_notify_register()函數(shù)調(diào)用
5)通過attachCategories函數(shù)將合并后的分類數(shù)據(jù)(方法烁设、屬性、協(xié)議)凡壤,插入到類原來(lái)數(shù)據(jù)的前面
分類加載流程詳細(xì)
category加載流程(懶加載和非懶加載load方法的不同)-
主類和分類中有同名方法署尤,如何調(diào)用主類方法?
- 類方法亚侠,因?yàn)榉诸惒]有覆蓋主類的同名方法曹体,只是Category的方法排在方法列表前面,而主類的方法被移到了方法列表的后面硝烂。 我們可以利用Runtime提供的API箕别,從方法列表里拿回原方法,從而調(diào)用滞谢。如下所示
-(void) exchangeMainCategoryMethod:(id)target selector:(SEL)selector{
// Get the class method list
uint count;
Method *methodList = class_copyMethodList([target class], &count);
Method firstMethod = nil ;
Method lastMethod = nil ;
for (int i = 0; i < count; i++) {
Method method = methodList[i];
NSLog(@"Category catch selector : %d %@", i, NSStringFromSelector(method_getName(method)));
SEL name = method_getName(method);
if (name == selector) {
if(!firstMethod){
firstMethod = method ;
lastMethod = method ;
}else{
lastMethod = method ;
}
}
}
if(firstMethod && lastMethod && firstMethod!=lastMethod){
method_exchangeImplementations(firstMethod, lastMethod) ;
}
}
在調(diào)用方法前串稀,先將主類和分類的IMP交換,將主類的IMP放在前面狮杨,分類的IMP放在后面母截,就會(huì)優(yōu)先執(zhí)行主類的方法。方法調(diào)用完后橄教,再將主類和分類的IMP交換清寇,恢復(fù)初始的IMP順序。
- 如果是實(shí)例方法护蝶,分類同名方法會(huì)覆蓋主類的方法华烟,獲取到的IMP永遠(yuǎn)是一個(gè),持灰,所以只能調(diào)用分類的方法盔夜,沒有辦法解決
關(guān)聯(lián)對(duì)象
- 本質(zhì),有幾層,如何運(yùn)作喂链?
在OC中返十,runtime提供了動(dòng)態(tài)添加屬性和獲得屬性的API:objc_setAssociatedObject
、objc_getAssociatedObject
至少是2層
關(guān)聯(lián)對(duì)象OC底層實(shí)現(xiàn)
iOS 關(guān)聯(lián)對(duì)象的實(shí)現(xiàn)原理
- 關(guān)聯(lián)對(duì)象的key為什么void
用void 來(lái)修飾key衩藤,更小的分配內(nèi)存
擴(kuò)展Extension
- 使用場(chǎng)景
- 聲明私有屬性
- 聲明私有方法
- 聲明私有成員變量
- 特點(diǎn)
- 它是編譯時(shí)決議
- 只以聲明的形式存在吧慢,一般都寫在.m中
- 不能為系統(tǒng)類添加擴(kuò)展
- 擴(kuò)展與分類的區(qū)別
- 分類有名字,擴(kuò)展沒有名字赏表,是一個(gè)匿名的分類
- 分類是運(yùn)行時(shí)決議检诗,而擴(kuò)展是編譯時(shí)決議;
所以分類中的方法沒有實(shí)現(xiàn)不會(huì)警告瓢剿,而擴(kuò)展聲明的方法不實(shí)現(xiàn)會(huì)被警告逢慌。 - 分類原則上可以增加屬性,實(shí)例方法间狂,類方法攻泼,而且外部類是可以訪問的。擴(kuò)展能添加屬性鉴象,方法忙菠,實(shí)例變量,默認(rèn)是不對(duì)外公開的纺弊。
- 分類有自己的實(shí)現(xiàn)部分牛欢,擴(kuò)展沒有自己的實(shí)現(xiàn)部分,只能依賴對(duì)應(yīng)的類本身來(lái)實(shí)現(xiàn)淆游。
- 可以為系統(tǒng)類添加分類傍睹,而不能為系統(tǒng)類添加擴(kuò)展。
協(xié)議Protocol
可以定義哪些犹菱?
-
用Swift 將協(xié)議(protocol)中的部分方法設(shè)計(jì)成可選(optional)拾稳,該怎樣實(shí)現(xiàn)?
@optional
和@required
是Objective-C中特有的關(guān)鍵字腊脱。
Swift中访得,默認(rèn)所有方法在協(xié)議中都是必須實(shí)現(xiàn)的。而且陕凹,協(xié)議里方法不可以直接定義optional
震鹉。先給出兩種解決方案:- 在協(xié)議和方法前都加上
@objc
關(guān)鍵字,然后再在方法前加上optional
關(guān)鍵字捆姜。該方法實(shí)際上是把協(xié)議轉(zhuǎn)化為Objective-C的方式然后進(jìn)行可選定義 - 用擴(kuò)展(extension)來(lái)規(guī)定可選方法。Swift中迎膜,協(xié)議擴(kuò)展(protocol extension)可以定義部分方法的默認(rèn)實(shí)現(xiàn)泥技,這樣這些方法在實(shí)際調(diào)用中就是可選實(shí)現(xiàn)的了
用Swift 將協(xié)議(protocol)中的部分方法設(shè)計(jì)成可選(optional)
- 在協(xié)議和方法前都加上
NSNotification
KVO
iOS-底層原理 23:KVO 底層原理
iOS 設(shè)計(jì)模式(五)-KVO 詳解
屬性關(guān)鍵字
- 屬性關(guān)鍵字有哪些
iOS 屬性關(guān)鍵字 - 對(duì)比weak和assign
weak和assign的區(qū)別-正確使用weak、assign - weak是如何將指針置為nil?
iOS weak指針置nil具體過程 - 深拷貝 淺拷貝
iOS-深拷貝和淺拷貝
OC內(nèi)存管理相關(guān)
內(nèi)存五大分區(qū)
堆區(qū)、棧區(qū)珊豹、全局區(qū)簸呈、常量區(qū)、代碼區(qū)
內(nèi)存管理方案
- 有哪些方案店茶,分別是用于什么場(chǎng)景蜕便?
- TaggedPointer
對(duì)一些小對(duì)象,如NSNumber等,采用的是TaggedPointer這種內(nèi)存管理方案。 - NONPOINTER_ISA
對(duì)于64位架構(gòu)下的iOS應(yīng)用程序采用的是NONPOINTER_ISA這種內(nèi)存管理方案贩幻。
在64位架構(gòu)下,ISA這個(gè)指針本身是占64個(gè)bit位的,但其實(shí)有32位或者40位就夠用了,剩余的bit位其實(shí)是浪費(fèi)的,蘋果為了提高內(nèi)存的利用率,在iSA剩余的這些bit位當(dāng)中,存儲(chǔ)了一些關(guān)于內(nèi)存管理方面的相關(guān)內(nèi)容,這個(gè)叫非指針型的ISA轿腺。 - 散列表
是一種很復(fù)雜的結(jié)構(gòu),其中包含了引用計(jì)數(shù)表和弱引用表。
- TaggedPointer
iOS內(nèi)存布局&內(nèi)存管理方案&數(shù)據(jù)結(jié)構(gòu)
- 介紹NONOPINTER_ISA
isa分為POINTER_ISA(指針類型)和NONPOINTER_ISA(非指針類型)
POINTER_ISA指針類型只有一個(gè)內(nèi)存地址
NONPOINTER_ISA除了有地址丛楚,還包含其他字段:
//arm64 架構(gòu)
struct
{
uintptr_t nonpointer : 1; // 0:普通指針族壳,1:優(yōu)化過,使用位域存儲(chǔ)更多信息
uintptr_t has_assoc : 1; // 對(duì)象是否含有或曾經(jīng)含有關(guān)聯(lián)引用,如果沒有,則析構(gòu)時(shí)會(huì)更快
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析構(gòu)函數(shù)或OC的dealloc,如果沒有,則析構(gòu)時(shí)會(huì)更快
uintptr_t shiftcls : 33; // 類的指針,存放著 Class、Meta-Class 對(duì)象的內(nèi)存地址信息
uintptr_t magic : 6; // 固定值為 0xd2,用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向,如果沒有趣些,則析構(gòu)時(shí)更快
uintptr_t deallocating : 1; // 對(duì)象是否正在釋放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 來(lái)存儲(chǔ)引用計(jì)數(shù)
uintptr_t extra_rc : 19; // 引用計(jì)數(shù)能夠用 19 個(gè)二進(jìn)制位存儲(chǔ)時(shí)仿荆,直接存儲(chǔ)在這里
};
has_sidetable_rc表明該對(duì)象的引用計(jì)數(shù)器是否過大而無(wú)法儲(chǔ)存到isa指針,如果過大坏平,則其會(huì)存入相應(yīng)的sideTable(散列表)中拢操,正常則存入extra_rc中,且extra_rc保存的是引用計(jì)數(shù)減1后的結(jié)果
- 介紹散列表(哈希表)舶替,如何解決沖突
- sideTable結(jié)構(gòu)令境,有哪些SideTable?
- 為什么不是一個(gè)sideTable坎穿,用了什么方案展父?
iOS內(nèi)存布局&內(nèi)存管理方案&數(shù)據(jù)結(jié)構(gòu)
弱引用表
- 介紹
iOS底層原理探索 -- 內(nèi)存管理之弱引用表 - weak從引用到置于nil的整個(gè)流程
釋放時(shí),調(diào)?clearDeallocating函數(shù)玲昧。clearDeallocating函數(shù)?先根據(jù)對(duì)象地址
獲取所有weak指針地址的數(shù)組栖茉,然后遍歷這個(gè)數(shù)組把其中的數(shù)據(jù)設(shè)為nil,最后把這個(gè)
entry從weak表中刪除孵延,最后清理對(duì)象的記錄1.實(shí)現(xiàn)weak后吕漂,為什么對(duì)象釋放后會(huì)?動(dòng)為nil?
runtime
對(duì)注冊(cè)的類尘应, 會(huì)進(jìn)?布局惶凝,對(duì)于weak
對(duì)象會(huì)放??個(gè)hash
表中。
?weak
指向的對(duì)象內(nèi)存地址作為key
犬钢,當(dāng)此對(duì)象的引?計(jì)數(shù)為0
的時(shí)候會(huì)dealloc
苍鲜,假如weak
指向的對(duì)象內(nèi)存地址是a
,那么就會(huì)以a
為鍵玷犹, 在這個(gè)weak
表中搜索混滔,找到所有以a
為鍵的weak
對(duì)象,從?設(shè)置為nil
。
追問的問題?:
2.當(dāng)weak引?指向的對(duì)象被釋放時(shí)坯屿,?是如何去處理weak指針的呢油湖?
1、調(diào)?objc_release
2领跛、因?yàn)閷?duì)象的引?計(jì)數(shù)為0
乏德,所以執(zhí)?dealloc
3、在dealloc
中吠昭,調(diào)?了_objc_rootDealloc
函數(shù)
4喊括、在_objc_rootDealloc
中,調(diào)?了object_dispose
函數(shù)
5怎诫、調(diào)?objc_destructInstance
6瘾晃、最后調(diào)?objc_clear_deallocating
,詳細(xì)過程如下:
a. 從weak
表中獲取廢棄對(duì)象的地址為鍵值的記錄
b. 將包含在記錄中的所有附有weak
修飾符變量的地址,賦值為nil
c. 將weak
表中該記錄刪除
d. 從引?計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄
weak的一生
引用計(jì)數(shù)
-
什么是引用計(jì)數(shù)幻妓?
- 引用計(jì)數(shù)是一個(gè)簡(jiǎn)單而有效的管理對(duì)象生命周期的方式
- 當(dāng)我們創(chuàng)建一個(gè)新對(duì)象時(shí)蹦误,它的引用計(jì)數(shù)為1,當(dāng)有一個(gè)新的指針指向這個(gè)對(duì)象時(shí)肉津,引用計(jì)數(shù)+1强胰,當(dāng)指針不在指向這個(gè)對(duì)象時(shí),我們將其引用計(jì)數(shù)-1妹沙,當(dāng)對(duì)象的引用計(jì)數(shù)變?yōu)?時(shí)偶洋,說(shuō)明這個(gè)對(duì)象不再被任何指針指向了,這個(gè)時(shí)候我們就可以將這個(gè)對(duì)象銷毀距糖,回收內(nèi)存玄窝。
-
alloc的對(duì)象引用計(jì)數(shù)為多少?打印出來(lái)的話,引用計(jì)數(shù)為多少悍引?為什么恩脂?
- 引用計(jì)數(shù)的總值就是isa里面extra_rc的取值和散列表中引用計(jì)數(shù)表的取值外加1,我們新alloc的對(duì)象趣斤,引用計(jì)數(shù)打印為1就是因?yàn)檫@個(gè)加的這個(gè)1俩块,其實(shí)新alloc出來(lái)的對(duì)象引用計(jì)數(shù)為0
iOS-內(nèi)存管理(二)-引用計(jì)數(shù)
- 引用計(jì)數(shù)的總值就是isa里面extra_rc的取值和散列表中引用計(jì)數(shù)表的取值外加1,我們新alloc的對(duì)象趣斤,引用計(jì)數(shù)打印為1就是因?yàn)檫@個(gè)加的這個(gè)1俩块,其實(shí)新alloc出來(lái)的對(duì)象引用計(jì)數(shù)為0
Dealloc
-
具體描述dealloc的流程
image.png
iOS Dealloc流程解析 Dealloc 實(shí)現(xiàn)原理
- 關(guān)聯(lián)對(duì)象和弱引用對(duì)象需要我們手動(dòng)清理嗎?為什么浓领?
- 不需要
- 1.執(zhí)行了
object_cxxDestruct
函數(shù)
2.執(zhí)行_object_remove_assocations
,去除了關(guān)聯(lián)對(duì)象.(這也是為什么category添加屬性時(shí),在釋放時(shí)沒有必要remove)
3.就是上面寫的那個(gè),清空引用計(jì)數(shù)表并清除弱引用表,將weak
指針置為nil
object_cxxDestruct
是由編譯器生成,這個(gè)方法原本是為了++對(duì)象析構(gòu),ARC借用了這個(gè)方法插入代碼實(shí)現(xiàn)了自動(dòng)內(nèi)存釋放的工作.
自動(dòng)釋放池
- 什么是自動(dòng)釋放池玉凯?他的結(jié)構(gòu)是怎么樣的?
iOS的自動(dòng)釋放池(AutoReleasePool)- OC中的一種內(nèi)存自動(dòng)回收機(jī)制联贩,它可以延遲加入AutoreleasePool中的變量release的時(shí)機(jī)漫仆,即當(dāng)我們創(chuàng)建了一個(gè)對(duì)象,并把他加入到了自動(dòng)釋放池中時(shí)泪幌,他不會(huì)立即被釋放盲厌,會(huì)等到一次runloop結(jié)束或者作用域超出{}或者超出[pool release]之后再被釋放
- 自動(dòng)釋放池的數(shù)據(jù)結(jié)構(gòu):
是以棧為結(jié)點(diǎn)通過雙向鏈表的形式組合而成
- 自動(dòng)釋放池與線程的關(guān)系
- 一一對(duì)應(yīng)
- 描述自動(dòng)釋放池的運(yùn)作
iOS開發(fā)進(jìn)階:自動(dòng)釋放池的實(shí)現(xiàn)原理分析 - 一個(gè)對(duì)象在作用域結(jié)束后什么時(shí)候被釋放玄渗?
- 分兩種情況:?動(dòng)?預(yù)釋放時(shí)機(jī)、系統(tǒng)?動(dòng)去釋放狸眼。
?動(dòng)?預(yù)釋放時(shí)機(jī)--指定autoreleasepool 就是所謂的:當(dāng)前作?域?括號(hào)結(jié)束時(shí)釋
放。
系統(tǒng)?動(dòng)去釋放--不?動(dòng)指定autoreleasepool
Autorelease
對(duì)象出了作?域之后浴滴,會(huì)被添加到最近?次創(chuàng)建的?動(dòng)釋放池中拓萌,并會(huì)在
當(dāng)前的 runloop 迭代結(jié)束時(shí)釋放。
釋放的時(shí)機(jī)總結(jié)起來(lái)升略,可以?下圖來(lái)表示:
autoreleasepool與 runloop 的關(guān)系圖
image.png
- 分兩種情況:?動(dòng)?預(yù)釋放時(shí)機(jī)、系統(tǒng)?動(dòng)去釋放狸眼。
下?對(duì)這張圖進(jìn)?詳細(xì)的解釋:
從程序啟動(dòng)到加載完成是?個(gè)完整的運(yùn)?循環(huán)微王,然后會(huì)停下來(lái),等待?戶交互品嚣,?戶的每?次交互都會(huì)啟動(dòng)?次運(yùn)?循環(huán)炕倘,來(lái)處理?戶所有的點(diǎn)擊事件、觸摸事件翰撑。
我們都知道: 所有 autorelease
的對(duì)象罩旋,在出了作?域之后,會(huì)被?動(dòng)添加到最近創(chuàng)建的?動(dòng)釋放池中眶诈。
但是如果每次都放進(jìn)應(yīng)?程序的main.m
中的autoreleasepool
中涨醋,遲早有被撐滿的?刻。
這個(gè)過程中必定有?個(gè)釋放的動(dòng)作逝撬。何時(shí)浴骂?
在?次完整的運(yùn)?循環(huán)結(jié)束之前,會(huì)被銷毀宪潮。
那什么時(shí)間會(huì)創(chuàng)建?動(dòng)釋放池溯警?
運(yùn)?循環(huán)檢測(cè)到事件并啟動(dòng)后,就會(huì)創(chuàng)建?動(dòng)釋放池狡相。
?線程的 runloop 默認(rèn)是不?作梯轻,?法主動(dòng)創(chuàng)建,必須?動(dòng)創(chuàng)建谣光。
?定義的 NSOperation 和 NSThread 需要?動(dòng)創(chuàng)建?動(dòng)釋放池檩淋。?如: ?定義的
NSOperation 類中的 main ?法?就必須添加?動(dòng)釋放池。否則出了作?域后萄金,?動(dòng)釋放對(duì)象會(huì)因?yàn)闆]有?動(dòng)釋放池去處理它蟀悦,?造成內(nèi)存泄露。
但對(duì)于 blockOperation 和 invocationOperation 這種默認(rèn)的Operation 氧敢,系統(tǒng)已經(jīng)幫我們封裝好了日戈,不需要?動(dòng)創(chuàng)建?動(dòng)釋放池。
@autoreleasepool
當(dāng)?動(dòng)釋放池被銷毀或者耗盡時(shí)孙乖,會(huì)向?動(dòng)釋放池中的所有對(duì)象發(fā)送 release 消息浙炼,釋放?動(dòng)釋放池中的所有對(duì)象份氧。
如果在?個(gè)vc的viewDidLoad中創(chuàng)建?個(gè) Autorelease對(duì)象,那么該對(duì)象會(huì)在
viewDidAppear ?法執(zhí)?前就被銷毀了