以下內(nèi)容是本人在刷題過(guò)程中整理的一些題目, 答案或者來(lái)自原帖, 或者來(lái)自GPT,不一定保證正確,各位看官仔細(xì)斟酌
NSString 如何計(jì)算字符個(gè)數(shù)?
ChatGPT
NSString的length屬性是基于UTF-16編碼實(shí)現(xiàn)的。在UTF-16編碼中,一個(gè)Unicode字符可能占用一個(gè)或兩個(gè)16位的代碼單元(即一個(gè)或兩個(gè)UTF-16碼元)。因此,NSString的length屬> 性返回的是字符串中代碼單元的數(shù)量,而不是字符的實(shí)際數(shù)量。對(duì)于大多數(shù)情況下的常規(guī)字符,一個(gè)Unicode字符對(duì)應(yīng)一個(gè)UTF-16碼元,因此length屬性返回的值即為字符串中的字符數(shù)。但對(duì)于一些特殊字符,比如表情符號(hào)和一些輔助字符,一個(gè)Unicode字符可能會(huì)占用兩個(gè)UTF-16碼元,這時(shí)length屬性返回的值可能會(huì)大于字符實(shí)際的個(gè)數(shù)。
因此,雖然length屬性可以在大多數(shù)情況下準(zhǔn)確地計(jì)算字符串中的字符個(gè)數(shù),但在處理包含特殊字符的字符串時(shí),需要注意到這種編碼差異可能導(dǎo)致的計(jì)數(shù)不準(zhǔn)確的情況。
block為什么要是用copy修飾?
ChatGPT
在iOS中,當(dāng)你在一個(gè)Block內(nèi)部訪問(wèn)了外部的對(duì)象(例如局部變量或者對(duì)象屬性),這個(gè)Block會(huì)將這些外部對(duì)象進(jìn)行捕獲,以便在Block執(zhí)行時(shí)可以訪問(wèn)它們。默認(rèn)情況下,Block會(huì)在棧上創(chuàng)建,這意味著它們可以直接訪問(wèn)外部的變量。然而,如果這個(gè)Block在創(chuàng)建后,還會(huì)被傳遞到異步執(zhí)行的任務(wù)中(比如在網(wǎng)絡(luò)請(qǐng)求的回調(diào)中使用Block),那么當(dāng)任務(wù)執(zhí)行時(shí),這個(gè)Block所捕獲的外部對(duì)象可能已經(jīng)超出了其作用域,導(dǎo)致出現(xiàn)野指針錯(cuò)誤或者內(nèi)存泄漏。因?yàn)闂I系腂lock會(huì)隨著其作用域的結(jié)束而被銷毀,而異步任務(wù)可能在Block銷毀之后執(zhí)行,這就會(huì)造成問(wèn)題。
為了解決這個(gè)問(wèn)題,可以使用copy關(guān)鍵字來(lái)將Block從棧上復(fù)制到堆上。通過(guò)使用copy修飾符,Block會(huì)被復(fù)制到堆上,從而保證在異步任務(wù)執(zhí)行時(shí),Block所捕獲的外部對(duì)象依然存在。即使原始Block的棧上實(shí)例被銷毀,復(fù)制后的Block在堆上仍然可以繼續(xù)使用,并且能夠正確訪問(wèn)外部的對(duì)象。
因此,在iOS開發(fā)中,當(dāng)你需要在Block內(nèi)部訪問(wèn)外部的對(duì)象,并且這個(gè)Block可能會(huì)被傳遞到異步任務(wù)中時(shí),建議使用copy修飾符來(lái)確保代碼的正確性和安全性。
分類和擴(kuò)展有什么區(qū)別?可以分別用來(lái)做什么?分類有哪些局限性?分類的結(jié)構(gòu)體里面有哪些成員?
分類(Category)和擴(kuò)展(Extension)是Objective-C和Swift中的兩種不同的方式來(lái)擴(kuò)展類的功能。它們有一些相似之處,但也有一些區(qū)別。
分類(Category):
在Objective-C中,分類是一種將類的實(shí)現(xiàn)分散到多個(gè)文件中的方式。通過(guò)分類,可以為已有的類添加新的方法,而無(wú)需修改原始類的實(shí)現(xiàn)。分類可以用來(lái)實(shí)現(xiàn)以下功能:添加新方法: 可以向已有的類中添加新的實(shí)例方法、類方法或協(xié)議。
分組代碼: 可以將一個(gè)類的實(shí)現(xiàn)拆分到多個(gè)文件中,使代碼更加組織有序。
分類的局限性包括:無(wú)法添加實(shí)例變量: 分類中無(wú)法添加新的實(shí)例變量。
方法覆蓋: 如果在分類中定義了與原始類相同的方法,會(huì)覆蓋原始類的實(shí)現(xiàn)。
分類的結(jié)構(gòu)體里面一般包括以下成員:方法聲明和實(shí)現(xiàn): 在分類的實(shí)現(xiàn)文件中定義新增的方法。
頭文件聲明: 在分類的頭文件中聲明新增的方法,以便在其他地方使用。擴(kuò)展(Extension):
在Objective-C中,擴(kuò)展是一種特殊的匿名分類,它允許在類的實(shí)現(xiàn)文件中添加額外的方法、屬性和成員變量,但是這些擴(kuò)展只能在當(dāng)前類的實(shí)現(xiàn)文件中使用,無(wú)法在其他文件中引用。擴(kuò)展可以用來(lái)實(shí)現(xiàn)以下功能:添加私有方法和屬性: 可以在類的實(shí)現(xiàn)文件中添加私有的方法和屬性,使其只能在當(dāng)前類的實(shí)現(xiàn)中訪問(wèn)。
遵循協(xié)議: 可以在擴(kuò)展中實(shí)現(xiàn)某些協(xié)議,以隱藏實(shí)現(xiàn)細(xì)節(jié)并將其與類的接口分離。
擴(kuò)展的結(jié)構(gòu)體里面一般包括以下成員:方法聲明和實(shí)現(xiàn): 在擴(kuò)展的實(shí)現(xiàn)文件中定義新增的方法。
私有屬性聲明和實(shí)現(xiàn): 在擴(kuò)展的實(shí)現(xiàn)文件中添加新增的私有屬性和實(shí)現(xiàn)。
在Swift中,擴(kuò)展的概念更加強(qiáng)大,它可以用來(lái)實(shí)現(xiàn)類似于Objective-C中分類和擴(kuò)展的功能,但更加靈活和統(tǒng)一。在Swift中,擴(kuò)展可以用來(lái)添加新的方法、計(jì)算屬性、下標(biāo)、協(xié)議實(shí)現(xiàn)等功能,而且可以在任何地方使用,不受限于類的實(shí)現(xiàn)文件。因此,Swift中的擴(kuò)展更加推薦使用。
講一下atomic的實(shí)現(xiàn)機(jī)制;為什么不能保證絕對(duì)的線程安全(最好可以結(jié)合場(chǎng)景來(lái)說(shuō))?
atomic是Objective-C中用于聲明屬性的一個(gè)修飾符,用來(lái)指定該屬性在多線程環(huán)境下的行為。當(dāng)使用atomic修飾符時(shí),編譯器會(huì)自動(dòng)生成線程安全的訪問(wèn)方法,以確保在多個(gè)線程同時(shí)訪問(wèn)屬性時(shí)不會(huì)發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。其實(shí)現(xiàn)機(jī)制通常是通過(guò)在訪問(wèn)方法中使用鎖機(jī)制(如@synchronized)來(lái)確保屬性的讀取和寫入操作的原子性。
然而,盡管atomic可以提供一定程度上的線程安全性,但它并不能保證絕對(duì)的線程安全。這是因?yàn)榧词故褂昧随i機(jī)制,仍然存在一些情況會(huì)導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)和不一致的問(wèn)題。
一個(gè)典型的場(chǎng)景是:假設(shè)有兩個(gè)線程同時(shí)對(duì)一個(gè)對(duì)象的屬性進(jìn)行讀取和寫入操作,而這兩個(gè)操作并不是原子性的。在atomic修飾下,編譯器會(huì)為屬性的訪問(wèn)方法加上鎖,以保證同一時(shí)間只有一個(gè)線程能夠執(zhí)行屬性的讀取或?qū)懭氩僮?。然而,如果其中一個(gè)線程正在執(zhí)行寫入操作時(shí),另一個(gè)線程同時(shí)進(jìn)行讀取操作,由于讀取操作并不受鎖的保護(hù),它可能會(huì)讀取到屬性的中間狀態(tài),導(dǎo)致數(shù)據(jù)不一致性。
因此,盡管atomic修飾符可以提供一定程度上的線程安全性,但在一些特定的并發(fā)場(chǎng)景下,仍然需要開發(fā)者自己實(shí)現(xiàn)額外的線程同步機(jī)制來(lái)確保數(shù)據(jù)的完整性和一致性。另外,值得注意的是,使用atomic修飾符會(huì)帶來(lái)一定的性能開銷,因?yàn)槊看卧L問(wèn)屬性都需要進(jìn)行鎖的獲取和釋放操作,所以在某些情況下,使用atomic并不是最優(yōu)的選擇。
- 為什么讀取操作不受到鎖的保護(hù)?(上一題的引申)
atomic修飾符所提供的線程安全保證主要是針對(duì)屬性的寫入操作的。讀取操作通常是不會(huì)改變屬性的值的,因此不需要額外的保護(hù)。如果每次讀取操作都需要獲取鎖,將會(huì)帶來(lái)性能的損耗,并且會(huì)降低并發(fā)訪問(wèn)的效率。
被weak修飾的對(duì)象在被釋放的時(shí)候會(huì)發(fā)生什么?是如何實(shí)現(xiàn)的?知道sideTable么?里面的結(jié)構(gòu)可以畫出來(lái)么?
當(dāng)一個(gè)對(duì)象被weak修飾時(shí),它的引用計(jì)數(shù)不會(huì)增加,因此并不會(huì)影響對(duì)象的生命周期。當(dāng)被weak修飾的對(duì)象所指向的對(duì)象被釋放時(shí),該weak指針會(huì)被自動(dòng)置為nil,以防止出現(xiàn)野指針訪問(wèn)。
weak指針的實(shí)現(xiàn)通常依賴于Objective-C的Runtime機(jī)制中的side table。side table是一個(gè)哈希表,用于存儲(chǔ)weak指針的引用信息。每當(dāng)一個(gè)對(duì)象被設(shè)置為weak指針?biāo)玫哪繕?biāo)時(shí),Runtime會(huì)將這個(gè)weak指針的地址作為key,被引用對(duì)象的地址作為value,存儲(chǔ)到side table中。當(dāng)被引用的對(duì)象被釋放時(shí),Runtime會(huì)遍歷side table,將所有指向該對(duì)象的weak指針的值置為nil。
下面是side table的簡(jiǎn)化結(jié)構(gòu)示意圖:
+---------------------------+
| Bucket 0 |
+---------------------------+
| Weak Pointer Address 1 |
| Weak Pointer Address 2 |
| ... |
+---------------------------+
| Bucket 1 |
+---------------------------+
| Weak Pointer Address 3 |
| Weak Pointer Address 4 |
| ... |
+---------------------------+
| ... |
+---------------------------+
side table是一個(gè)哈希表,它由多個(gè)桶(bucket)組成,每個(gè)桶存儲(chǔ)一組weak指針的引用信息。每個(gè)桶中存儲(chǔ)了多個(gè)weak指針的地址,這些weak指針都指向同一個(gè)對(duì)象。當(dāng)對(duì)象被釋放時(shí),Runtime會(huì)遍歷side table中的每個(gè)桶,將所有指向該對(duì)象的weak指針的值置為nil。
這樣一來(lái),被weak修飾的對(duì)象的引用計(jì)數(shù)不會(huì)增加,但在其所指向的對(duì)象被釋放時(shí),所有指向該對(duì)象的weak指針都會(huì)被自動(dòng)置為nil,以避免野指針訪問(wèn)。
關(guān)聯(lián)對(duì)象有什么應(yīng)用,系統(tǒng)如何管理關(guān)聯(lián)對(duì)象?其被釋放的時(shí)候需要手動(dòng)將所有的關(guān)聯(lián)對(duì)象的指針置空么?
關(guān)聯(lián)對(duì)象(Associated Objects)是Objective-C中的一種機(jī)制,允許開發(fā)者向現(xiàn)有的類動(dòng)態(tài)添加額外的實(shí)例變量。關(guān)聯(lián)對(duì)象的主要應(yīng)用包括:
為現(xiàn)有的類添加屬性:通過(guò)關(guān)聯(lián)對(duì)象,可以在不修改原始類的情況下,為其添加新的屬性。
在分類中添加屬性:由于分類無(wú)法直接添加實(shí)例變量,可以使用關(guān)聯(lián)對(duì)象來(lái)實(shí)現(xiàn)在分類中添加屬性的功能。
實(shí)現(xiàn)代理模式:可以將代理對(duì)象關(guān)聯(lián)到另一個(gè)對(duì)象上,從而實(shí)現(xiàn)代理模式。
系統(tǒng)是如何管理關(guān)聯(lián)對(duì)象的呢?在Objective-C的Runtime中,每個(gè)類都有一個(gè)關(guān)聯(lián)對(duì)象的哈希表(Associative References Table)。當(dāng)通過(guò)objc_setAssociatedObject函數(shù)將一個(gè)對(duì)象關(guān)聯(lián)到另一個(gè)對(duì)象時(shí),Runtime會(huì)將這個(gè)關(guān)聯(lián)對(duì)象的地址作為key,被關(guān)聯(lián)對(duì)象的地址作為value,存儲(chǔ)到關(guān)聯(lián)對(duì)象的哈希表中。當(dāng)通過(guò)objc_getAssociatedObject函數(shù)獲取關(guān)聯(lián)對(duì)象時(shí),Runtime會(huì)根據(jù)key從哈希表中找到對(duì)應(yīng)的關(guān)聯(lián)對(duì)象。關(guān)聯(lián)對(duì)象的釋放通常是自動(dòng)管理的。當(dāng)一個(gè)對(duì)象被釋放時(shí),Runtime會(huì)自動(dòng)遍歷其關(guān)聯(lián)對(duì)象的哈希表,將所有關(guān)聯(lián)對(duì)象的指針置為nil,以避免出現(xiàn)野指針訪問(wèn)。因此,開發(fā)者不需要手動(dòng)將關(guān)聯(lián)對(duì)象的指針置空。需要注意的是,由于ARC機(jī)制的存在,當(dāng)對(duì)象的引用計(jì)數(shù)為0時(shí),對(duì)象會(huì)被釋放,而關(guān)聯(lián)對(duì)象的指針也會(huì)被置為nil,這樣可以確保在釋放對(duì)象后不會(huì)出現(xiàn)野指針訪問(wèn)的情況。
KVO的底層實(shí)現(xiàn)?如何取消系統(tǒng)默認(rèn)的KVO并手動(dòng)觸發(fā)(給KVO的觸發(fā)設(shè)定條件:改變的值符合某個(gè)條件時(shí)再觸發(fā)KVO)?
KVO(Key-Value Observing)是Objective-C中的一種觀察者模式,用于監(jiān)聽對(duì)象屬性值的變化。它的底層實(shí)現(xiàn)依賴于Objective-C的Runtime機(jī)制。
底層實(shí)現(xiàn)原理:
當(dāng)你添加一個(gè)觀察者(Observer)時(shí),Runtime會(huì)動(dòng)態(tài)創(chuàng)建一個(gè)繼承自被觀察對(duì)象類的中間類。
這個(gè)中間類會(huì)重寫被觀察屬性的setter方法,以在屬性值發(fā)生變化時(shí)發(fā)送通知。
在setter方法中,會(huì)調(diào)用willChangeValueForKey:和didChangeValueForKey:方法,通知觀察者屬性值將要改變和已經(jīng)改變。
- 取消系統(tǒng)默認(rèn)的KVO
// 關(guān)閉自動(dòng)通知機(jī)制
[MyClass setAutomaticallyNotifiesObserversForKey:@"propertyName"];
// 修改屬性值,此時(shí)不會(huì)觸發(fā) KVO
object.propertyName = newValue;
// 打開自動(dòng)通知機(jī)制
[MyClass setAutomaticallyNotifiesObserversForKey:@"propertyName"];
- 手動(dòng)觸發(fā)
// 移除觀察者
[object removeObserver:self forKeyPath:@"propertyName"];
// 重新實(shí)現(xiàn)屬性的 setter 方法
- (void)setPropertyName:(NSString *)propertyName {
// 自定義屬性設(shè)置邏輯
_propertyName = propertyName;
// 手動(dòng)觸發(fā) KVO 通知,如果需要的話
[self willChangeValueForKey:@"propertyName"];
[self didChangeValueForKey:@"propertyName"];
}
講一下對(duì)象,類對(duì)象,元類,跟元類結(jié)構(gòu)體的組成以及他們是如何相關(guān)聯(lián)的?為什么對(duì)象方法沒有保存的對(duì)象結(jié)構(gòu)體里,而是保存在類對(duì)象的結(jié)構(gòu)體里?
在 Objective-C 中,對(duì)象、類對(duì)象、元類以及根元類(Root Metaclass)是組成類和對(duì)象系統(tǒng)的核心組件。它們之間的關(guān)系是相互關(guān)聯(lián)的,而這種關(guān)聯(lián)是通過(guò)指針來(lái)實(shí)現(xiàn)的。
對(duì)象(Object):
對(duì)象代表了內(nèi)存中實(shí)際的實(shí)例,存儲(chǔ)了實(shí)例變量的值以及指向其類對(duì)象的指針。類對(duì)象(Class Object):
類對(duì)象代表了類本身,存儲(chǔ)了類的實(shí)例方法列表、屬性列表等信息,并且包含指向元類的指針。元類(Metaclass):
元類是類對(duì)象的類,它存儲(chǔ)了類方法列表、協(xié)議列表等信息,并且包含指向根元類的指針。根元類(Root Metaclass):
根元類是所有元類的父類,它存儲(chǔ)了所有類共享的類方法列表等信息。
- 這些結(jié)構(gòu)體之間的關(guān)系可以通過(guò)下圖來(lái)說(shuō)明:
+--------------+ isa +--------------+ isa +--------------+ isa +--------------+
| Object | --------> | Class Object| --------> | Metaclass | --------> | Root Metaclass |
+--------------+ +--------------+ +--------------+ +--------------+
對(duì)象的方法并不是直接存儲(chǔ)在對(duì)象結(jié)構(gòu)體中的,而是存儲(chǔ)在類對(duì)象的方法列表中。這是因?yàn)閷?duì)象的方法通常是類共享的,所有同一個(gè)類的所有實(shí)例都共享相同的方法列表。因此,將方法存儲(chǔ)在類對(duì)象中可以節(jié)省內(nèi)存,同時(shí)也方便了方法的查找和共享。
另外,類對(duì)象中還包含了指向其元類的指針。通過(guò)這個(gè)指針,類對(duì)象可以訪問(wèn)元類中的類方法列表,實(shí)現(xiàn)類方法的調(diào)用。元類中也包含了指向根元類的指針,形成了一個(gè)鏈?zhǔn)浇Y(jié)構(gòu),用于查找和調(diào)用類方法。
總的來(lái)說(shuō),對(duì)象、類對(duì)象、元類以及根元類之間通過(guò)指針相互關(guān)聯(lián),共同構(gòu)成了 Objective-C 中的類和對(duì)象系統(tǒng),實(shí)現(xiàn)了方法的共享和繼承。
class_ro_t 和 class_rw_t 的區(qū)別?
class_ro_t 和 class_rw_t 是 Objective-C 中用于描述類的結(jié)構(gòu)體,它們?cè)谶\(yùn)行時(shí)扮演了不同的角色,具有不同的屬性和功能。
class_ro_t(Class RO Data,只讀數(shù)據(jù))
class_ro_t 結(jié)構(gòu)體存儲(chǔ)了類的只讀數(shù)據(jù),包括類的實(shí)例變量、屬性、方法列表等靜態(tài)信息。這些信息在類加載時(shí)就已經(jīng)確定,并且在整個(gè)程序運(yùn)行過(guò)程中保持不變。
具體屬性包括:
const char *name:類名。
const struct class *superclass:指向父類的指針。
uint32_t method_count:方法列表中方法的數(shù)量。
const struct method_list_t *method_list:指向方法列表的指針。
uint32_t ivar_count:實(shí)例變量列表中變量的數(shù)量。
const struct ivar_list_t *ivar_list:指向?qū)嵗兞苛斜淼闹羔槨?br> ...class_rw_t(Class RW Data,讀寫數(shù)據(jù))
class_rw_t 結(jié)構(gòu)體存儲(chǔ)了類的讀寫數(shù)據(jù),包括類的可變信息,例如在運(yùn)行時(shí)動(dòng)態(tài)添加的方法、屬性等。
具體屬性包括:
uint32_t flags:標(biāo)志位,描述類的一些特性,例如是否支持 ARC、是否是元類等。
uint32_t version:類的版本號(hào)。
const struct method_list_t *methods:指向方法列表的指針。
const struct property_list_t *properties:指向?qū)傩粤斜淼闹羔槨?br> const struct protocol_list_t *protocols:指向協(xié)議列表的指針。
...區(qū)別:
數(shù)據(jù)內(nèi)容: class_ro_t 存儲(chǔ)了類的只讀數(shù)據(jù),包括靜態(tài)信息;而 class_rw_t 存儲(chǔ)了類的讀寫數(shù)據(jù),包括動(dòng)態(tài)信息。
可變性: class_ro_t 中的數(shù)據(jù)在類加載后不可更改,是只讀的;而 class_rw_t 中的數(shù)據(jù)可以在運(yùn)行時(shí)動(dòng)態(tài)地添加或修改,是可讀寫的。
使用場(chǎng)景: class_ro_t 通常用于描述類的靜態(tài)信息,如在編譯時(shí)確定的方法列表、屬性列表等;而 class_rw_t 用于描述類的動(dòng)態(tài)信息,如在運(yùn)行時(shí)動(dòng)態(tài)添加的方法、屬性等。總的來(lái)說(shuō),class_ro_t 和 class_rw_t 分別描述了類的靜態(tài)信息和動(dòng)態(tài)信息,它們?cè)谶\(yùn)行時(shí)起到了不同的作用,共同構(gòu)成了 Objective-C 類的完整描述。
Autoreleasepool所使用的數(shù)據(jù)結(jié)構(gòu)是什么?AutoreleasePoolPage結(jié)構(gòu)體了解么?
Autorelease Pool(自動(dòng)釋放池)是 Objective-C 中用來(lái)管理自動(dòng)釋放對(duì)象的機(jī)制。在運(yùn)行過(guò)程中,當(dāng)你創(chuàng)建一個(gè)自動(dòng)釋放對(duì)象時(shí),這個(gè)對(duì)象會(huì)被添加到當(dāng)前的自動(dòng)釋放池中,當(dāng)自動(dòng)釋放池被銷毀時(shí),池中的所有對(duì)象都會(huì)被釋放。
AutoreleasePoolPage 是用來(lái)實(shí)現(xiàn)自動(dòng)釋放池的數(shù)據(jù)結(jié)構(gòu)。它是一個(gè)鏈表節(jié)點(diǎn),用于管理自動(dòng)釋放池中的對(duì)象。每個(gè) AutoreleasePoolPage 包含一個(gè)數(shù)組,用于存儲(chǔ)自動(dòng)釋放對(duì)象的指針。當(dāng)數(shù)組滿了之后,會(huì)通過(guò)指針的形式連接到下一個(gè) AutoreleasePoolPage,形成鏈表結(jié)構(gòu)。
AutoreleasePoolPage 結(jié)構(gòu)體通常包括以下成員:
id *next:指向下一個(gè) AutoreleasePoolPage 的指針,形成鏈表結(jié)構(gòu)。
uint32_t const capacity:當(dāng)前頁(yè)的容量,即可以存儲(chǔ)的自動(dòng)釋放對(duì)象的數(shù)量上限。
uint32_t count:當(dāng)前頁(yè)已經(jīng)存儲(chǔ)的自動(dòng)釋放對(duì)象的數(shù)量。
struct _opaque_data:用于存儲(chǔ)自動(dòng)釋放對(duì)象的數(shù)組。
這樣,當(dāng)你創(chuàng)建一個(gè)自動(dòng)釋放池時(shí),實(shí)際上是在內(nèi)存中創(chuàng)建了一個(gè) AutoreleasePoolPage 結(jié)構(gòu)體,并將其作為棧頂?shù)淖詣?dòng)釋放池。每次向自動(dòng)釋放池中添加對(duì)象時(shí),都是將對(duì)象的指針添加到當(dāng)前頁(yè)的數(shù)組中,當(dāng)當(dāng)前頁(yè)的數(shù)組滿了之后,會(huì)創(chuàng)建一個(gè)新的 AutoreleasePoolPage 并將其鏈接到鏈表中。當(dāng)自動(dòng)釋放池被銷毀時(shí),會(huì)依次釋放鏈表中的每個(gè) AutoreleasePoolPage,從而釋放池中的所有對(duì)象。總的來(lái)說(shuō),AutoreleasePoolPage 是用來(lái)實(shí)現(xiàn)自動(dòng)釋放池的數(shù)據(jù)結(jié)構(gòu),它通過(guò)鏈表的形式管理自動(dòng)釋放對(duì)象,并在適當(dāng)?shù)臅r(shí)候進(jìn)行內(nèi)存的回收和釋放。
iOS 中內(nèi)省的幾個(gè)方法?class方法和objc_getClass方法有什么區(qū)別?
在 iOS 中,內(nèi)?。╥ntrospection)是指在運(yùn)行時(shí)獲取對(duì)象、類、協(xié)議等信息的能力。以下是 iOS 中常用的內(nèi)省方法:
[NSObject isKindOfClass:]: 用于判斷一個(gè)對(duì)象是否是指定類或其子類的實(shí)例。
[NSObject isMemberOfClass:]: 用于判斷一個(gè)對(duì)象是否是指定類的實(shí)例(不包括其子類)。
[NSObject respondsToSelector:]: 用于判斷一個(gè)對(duì)象是否響應(yīng)指定的方法。
[NSObject conformsToProtocol:]: 用于判斷一個(gè)類是否遵循指定的協(xié)議。
這些方法可以幫助我們?cè)谶\(yùn)行時(shí)動(dòng)態(tài)地獲取對(duì)象的信息,從而進(jìn)行靈活的編程。關(guān)于 class 方法和 objc_getClass 函數(shù)的區(qū)別如下:
class 方法: 是 NSObject 類的一個(gè)實(shí)例方法,用于獲取對(duì)象的類對(duì)象。例如,[obj class] 會(huì)返回 obj 的類對(duì)象。
objc_getClass 函數(shù): 是 Objective-C Runtime 中的一個(gè)函數(shù),用于獲取指定類的類對(duì)象。它接受一個(gè)類名的 C 字符串作為參數(shù),返回對(duì)應(yīng)的類對(duì)象。例如,objc_getClass("ClassName") 會(huì)返回名為 "ClassName" 的類的類對(duì)象。
主要的區(qū)別在于使用方式和返回值的類型。class 方法是一個(gè)對(duì)象的實(shí)例方法,用于獲取該對(duì)象的類對(duì)象,而 objc_getClass 函數(shù)是一個(gè) C 函數(shù),用于直接獲取指定類的類對(duì)象。
一個(gè)int變量被__block修飾與否的區(qū)別?
在 Objective-C 中,__block 修飾符通常用于在 Block 內(nèi)部修改局部變量的值,并且使得變量的值在 Block 內(nèi)外保持一致。對(duì)于基本數(shù)據(jù)類型如 int,__block 修飾符的使用與否會(huì)有以下區(qū)別:
- 被 __block 修飾的變量:
- 可以在 Block 內(nèi)部被修改,并且修改后的值會(huì)影響到外部變量的值。
在 Block 內(nèi)部和外部是同一個(gè)變量,即共享同一個(gè)內(nèi)存地址。- 在 Block 內(nèi)部和外部是同一個(gè)變量,即共享同一個(gè)內(nèi)存地址。
__block int x = 10;
void (^block)(void) = ^{
x = 20; // 修改 x 的值
};
block();
NSLog(@"x = %d", x); // 輸出 x = 202.未被 __block 修飾的變量:
- 不能在 Block 內(nèi)部被修改,否則會(huì)編譯錯(cuò)誤。
- 在 Block 內(nèi)部和外部是不同的變量,即 Block 內(nèi)部創(chuàng)建了一個(gè)變量的拷貝。
int x = 10;
void (^block)(void) = ^{
// x = 20; // 編譯錯(cuò)誤,不能修改未被 __block 修飾的變量
};
block();
NSLog(@"x = %d", x); // 輸出 x = 10
為什么在block外部使用__weak修飾的同時(shí)需要在內(nèi)部使用__strong修飾?
在 Objective-C 中,經(jīng)常會(huì)在 Block 內(nèi)部引用外部的對(duì)象,為了避免循環(huán)引用(retain cycle),通常會(huì)使用 __weak 修飾符來(lái)修飾外部的強(qiáng)引用對(duì)象,以避免強(qiáng)引用循環(huán)導(dǎo)致內(nèi)存泄漏。
使用 __weak 修飾符可以解決循環(huán)引用的問(wèn)題,但同時(shí)也會(huì)帶來(lái)一個(gè)新的問(wèn)題,就是在 Block 內(nèi)部使用被 __weak 修飾的對(duì)象時(shí),由于對(duì)象是弱引用,可能會(huì)在 Block 執(zhí)行期間被釋放,導(dǎo)致訪問(wèn)懸空對(duì)象而產(chǎn)生異常。
為了解決這個(gè)問(wèn)題,通常會(huì)在 Block 內(nèi)部使用 __strong 修飾符來(lái)修飾被 __weak 修飾的對(duì)象。這樣做的原因是,__strong 修飾符可以使得在 Block 執(zhí)行期間弱引用對(duì)象保持有效,從而避免訪問(wèn)懸空對(duì)象的問(wèn)題。
總的來(lái)說(shuō),在 Block 內(nèi)部使用 __weak 修飾外部對(duì)象是為了避免循環(huán)引用,而在使用外部對(duì)象時(shí)需要使用 __strong 修飾符來(lái)確保對(duì)象在 Block 執(zhí)行期間保持有效。這種組合的使用方式可以有效地解決循環(huán)引用和訪問(wèn)懸空對(duì)象的問(wèn)題。
RunLoop的作用是什么?它的內(nèi)部工作機(jī)制了解么?(最好結(jié)合線程和內(nèi)存管理來(lái)說(shuō))
RunLoop(運(yùn)行循環(huán))是 iOS 和 macOS 中的一種事件處理機(jī)制,它主要用于處理各種輸入源(包括事件、定時(shí)器、消息等)并管理線程的消息循環(huán)。RunLoop 的作用包括但不限于以下幾點(diǎn):
處理事件: RunLoop 可以處理用戶輸入、定時(shí)器、網(wǎng)絡(luò)請(qǐng)求等各種事件,保證這些事件可以及時(shí)得到處理。
節(jié)省 CPU 資源: RunLoop 可以使線程進(jìn)入休眠狀態(tài),直到有事件需要處理時(shí)才喚醒線程,從而節(jié)省 CPU 資源,提高性能。
管理定時(shí)器: RunLoop 可以管理定時(shí)器,使得定時(shí)器可以按照預(yù)定的時(shí)間間隔觸發(fā)。
線程間通信: RunLoop 可以在多線程編程中進(jìn)行線程間的通信,使得不同線程之間可以傳遞消息、共享數(shù)據(jù)。RunLoop 的內(nèi)部工作機(jī)制如下:
RunLoop 啟動(dòng): 當(dāng)一個(gè)線程需要執(zhí)行事件循環(huán)時(shí),會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的 RunLoop 對(duì)象,并調(diào)用 run 或 runUntilDate: 方法啟動(dòng) RunLoop。
進(jìn)入事件循環(huán): 一旦 RunLoop 啟動(dòng),它會(huì)進(jìn)入一個(gè)事件循環(huán)(Event Loop)中,不斷地處理各種輸入源和定時(shí)器事件,直到被手動(dòng)停止或者沒有事件需要處理時(shí)才退出循環(huán)。
處理事件: 在事件循環(huán)中,RunLoop 會(huì)不斷地從輸入源中獲取事件,并調(diào)用相應(yīng)的處理方法來(lái)處理這些事件,比如響應(yīng)用戶輸入、執(zhí)行定時(shí)器任務(wù)等。
休眠和喚醒: 如果沒有事件需要處理,RunLoop 會(huì)使線程進(jìn)入休眠狀態(tài),直到有新的事件到來(lái)時(shí)才喚醒線程繼續(xù)處理事件。
定時(shí)器管理: RunLoop 會(huì)管理定時(shí)器,根據(jù)定時(shí)器設(shè)定的時(shí)間間隔觸發(fā)定時(shí)器任務(wù),并將任務(wù)添加到事件隊(duì)列中等待處理。RunLoop 在線程和內(nèi)存管理中的作用體現(xiàn)在以下幾個(gè)方面:
線程管理: RunLoop 可以管理線程的事件循環(huán),使得線程可以在需要時(shí)進(jìn)行事件處理,而在空閑時(shí)保持休眠狀態(tài),從而節(jié)省 CPU 資源,提高性能。
內(nèi)存管理: RunLoop 可以處理各種輸入源的事件,包括定時(shí)器、網(wǎng)絡(luò)請(qǐng)求等,可以在事件到來(lái)時(shí)觸發(fā)相應(yīng)的處理方法,從而及時(shí)釋放不再需要的內(nèi)存資源,避免內(nèi)存泄漏和內(nèi)存過(guò)大的問(wèn)題。
什么是離屏渲染, 什么場(chǎng)景會(huì)觸發(fā)離屏渲染?
離屏渲染(Offscreen Rendering)是指將圖像渲染到屏幕之外的緩沖區(qū)中,然后再將渲染好的圖像復(fù)制到屏幕上顯示的過(guò)程。離屏渲染通常會(huì)帶來(lái)額外的性能消耗,因此在開發(fā)中需要盡量避免過(guò)度使用離屏渲染。
以下是一些可能觸發(fā)離屏渲染的場(chǎng)景:
- 圖層屬性的混合(Blending): 當(dāng)一個(gè)圖層的 opacity、corner radius、shadow、mask、group opacity 等屬性發(fā)生改變時(shí),如果該圖層需要與其下層圖層進(jìn)行混合,就會(huì)觸發(fā)離屏渲染。
- 圖層的遮罩(Masking): 當(dāng)一個(gè)圖層需要應(yīng)用遮罩(例如使用 CAShapeLayer 或 CALayer 的 mask 屬性)時(shí),就會(huì)觸發(fā)離屏渲染。
- 圖層的剪裁(Clipping): 當(dāng)一個(gè)圖層需要進(jìn)行剪裁(例如使用 CALayer 的 cornerRadius 屬性進(jìn)行圓角裁剪),就會(huì)觸發(fā)離屏渲染。
- OpenGL 或 Core Graphics 繪制: 當(dāng)使用 OpenGL 或 Core Graphics 直接繪制圖形時(shí),也可能會(huì)觸發(fā)離屏渲染。
- 模糊效果(Blur): 當(dāng)一個(gè)圖層需要應(yīng)用模糊效果(例如使用 UIVisualEffectView 或 UIBlurEffect)時(shí),就會(huì)觸發(fā)離屏渲染。
- 渲染到紋理(Render to Texture): 當(dāng)一個(gè)圖層需要將內(nèi)容渲染到紋理中,然后再將紋理渲染到屏幕上時(shí),就會(huì)觸發(fā)離屏渲染。
需要注意的是,雖然離屏渲染可以實(shí)現(xiàn)一些復(fù)雜的效果,但過(guò)度使用離屏渲染會(huì)帶來(lái)性能上的損耗,可能導(dǎo)致應(yīng)用的卡頓和耗電量增加。因此,在開發(fā)中應(yīng)盡量避免過(guò)度使用會(huì)觸發(fā)離屏渲染的場(chǎng)景,以提高應(yīng)用的性能和用戶體驗(yàn)。