② 內(nèi)存管理相關(guān)

一谜叹、 在 Obj-C 中廓旬,如何檢測(cè)內(nèi)存泄漏盯蝴?你知道哪些方式速侈?

目前我知道的方式有以下幾種

  • Memory Leaks
  • Alloctions
  • Analyse
  • Debug Memory Graph
  • MLeaksFinder

泄露的內(nèi)存主要有以下兩種:

  • Leak Memory 這種是忘記 Release 操作所泄露的內(nèi)存薄扁。
  • Abandon Memory 這種是循環(huán)引用剪返,無(wú)法釋放掉的內(nèi)存。

上面所說(shuō)的五種方式邓梅,其實(shí)前四種都比較麻煩脱盲,需要不斷地調(diào)試運(yùn)行,第五種是騰訊閱讀團(tuán)隊(duì)出品日缨,效果好一些钱反,感興趣的可以看一下這兩篇文章:

二、 在 MRC 下如何重寫(xiě)屬性的 Setter 和 Getter ?

  • setter
-(void)setBrand:(NSString *)brand{
//如果實(shí)例變量指向的地址和參數(shù)指向的地址不同
    if (_brand != brand)
    {
        //將實(shí)例變量的引用計(jì)數(shù)減一
        [_brand release];
       //將參數(shù)變量的引用計(jì)數(shù)加一,并賦值給實(shí)例變量
        _brand = [brand retain];
    }
}
  • getter
-(NSString *)brand{
    //將實(shí)例變量的引用計(jì)數(shù)加1后,添加自動(dòng)減1
    //作用,保證調(diào)用getter方法取值時(shí)可以取到值的同時(shí)在完全不需要使用后釋放
    return [[_brand retain] autorelease];
}
  • 重寫(xiě)dealloc
//MRC下 手動(dòng)釋放內(nèi)存 可重寫(xiě)dealloc但不要調(diào)用dealloc  會(huì)崩潰
-(void)dealloc{
    [_string release];
    //必須最后調(diào)用super dealloc
    [super  dealloc];
}

三匣距、循環(huán)引用

循環(huán)引用的實(shí)質(zhì):多個(gè)對(duì)象相互之間有強(qiáng)引用面哥,不能釋放讓系統(tǒng)回收。

如何解決循環(huán)引用毅待?

1尚卫、避免產(chǎn)生循環(huán)引用,通常是將 strong 引用改為 weak 引用恩静。
比如在修飾屬性時(shí)用weak
在block內(nèi)調(diào)用對(duì)象方法時(shí)焕毫,使用其弱引用蹲坷,這里可以使用兩個(gè)宏

#define WS(weakSelf)            __weak __typeof(&*self)weakSelf = self; // 弱引用

#define ST(strongSelf)          __strong __typeof(&*self)strongSelf = weakSelf; //使用這個(gè)要先聲明weakSelf

還可以使用__block來(lái)修飾變量
在 MRC 下,__block不會(huì)增加其引用計(jì)數(shù)邑飒,避免了循環(huán)引用
在 ARC 下循签,__block修飾對(duì)象會(huì)被強(qiáng)引用,無(wú)法避免循環(huán)引用疙咸,需要手動(dòng)解除县匠。

2、在合適時(shí)機(jī)去手動(dòng)斷開(kāi)循環(huán)引用撒轮。
通常我們使用第一種乞旦。

循環(huán)引用場(chǎng)景:

  • 自循環(huán)引用:對(duì)象強(qiáng)持有的屬性同時(shí)持有該對(duì)象

  • 相互循環(huán)引用
  • 多循環(huán)引用

1、代理(delegate)循環(huán)引用屬于相互循環(huán)引用

delegate 是iOS中開(kāi)發(fā)中比較常遇到的循環(huán)引用题山,一般在聲明delegate的時(shí)候都要使用弱引用 weak,或者assign,當(dāng)然怎么選擇使用assign還是weak兰粉,MRC的話(huà)只能用assign,在ARC的情況下最好使用weak顶瞳,因?yàn)閣eak修飾的變量在釋放后自動(dòng)指向nil玖姑,防止野指針存在

2、NSTimer循環(huán)引用屬于相互循環(huán)引用

在控制器內(nèi)慨菱,創(chuàng)建NSTimer作為其屬性焰络,由于定時(shí)器創(chuàng)建后也會(huì)強(qiáng)引用該控制器對(duì)象,那么該對(duì)象和定時(shí)器就相互循環(huán)引用了符喝。
如何解決呢闪彼?
這里我們可以使用手動(dòng)斷開(kāi)循環(huán)引用:
如果是不重復(fù)定時(shí)器,在回調(diào)方法里將定時(shí)器invalidate并置為nil即可协饲。
如果是重復(fù)定時(shí)器畏腕,在合適的位置將其invalidate并置為nil即可

3、block循環(huán)引用

一個(gè)簡(jiǎn)單的例子:

@property (copy, nonatomic) dispatch_block_t myBlock;
@property (copy, nonatomic) NSString *blockString;

- (void)testBlock {
    self.myBlock = ^() {
        NSLog(@"%@",self.blockString);
    };
}

由于block會(huì)對(duì)block中的對(duì)象進(jìn)行持有操作,就相當(dāng)于持有了其中的對(duì)象茉稠,而如果此時(shí)block中的對(duì)象又持有了該block郊尝,則會(huì)造成循環(huán)引用。
解決方案就是使用__weak修飾self即可

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {
        NSLog(@"%@",weakSelf.blockString);
 };
  • 并不是所有block都會(huì)造成循環(huán)引用战惊。
    只有被強(qiáng)引用了的block才會(huì)產(chǎn)生循環(huán)引用
    而比如dispatch_async(dispatch_get_main_queue(), ^ { }),[UIView animateWithDuration:1 animations:^ { }]這些系統(tǒng)方法等
    或者block并不是其屬性而是臨時(shí)變量,即棧block
[self testWithBlock:^{
    NSLog(@"%@",self);
}];

- (void)testWithBlock:(dispatch_block_t)block {
    block();
}

還有一種場(chǎng)景流昏,在block執(zhí)行開(kāi)始時(shí)self對(duì)象還未被釋放,而執(zhí)行過(guò)程中吞获,self被釋放了况凉,由于是用weak修飾的,那么weakSelf也被釋放了各拷,此時(shí)在block里訪(fǎng)問(wèn)weakSelf時(shí)刁绒,就可能會(huì)發(fā)生錯(cuò)誤(向nil對(duì)象發(fā)消息并不會(huì)崩潰,但也沒(méi)任何效果)烤黍。
對(duì)于這種場(chǎng)景知市,應(yīng)該在block中對(duì) 對(duì)象使用__strong修飾傻盟,使得在block期間對(duì) 對(duì)象持有,block執(zhí)行結(jié)束后嫂丙,解除其持有娘赴。

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {

        __strong __typeof(self) strongSelf = weakSelf;

        [strongSelf test];
 };

四、 說(shuō)一下什么是 懸垂指針跟啤?什么是 野指針?

懸垂指針
指針指向的內(nèi)存已經(jīng)被釋放了诽表,但是指針還存在,這就是一個(gè) 懸垂指針 或者說(shuō) 迷途指針

野指針
沒(méi)有進(jìn)行初始化的指針隅肥,其實(shí)都是 野指針

五竿奏、 說(shuō)一下對(duì) retain,copy,assign,weak,_Unsafe_Unretain 關(guān)鍵字的理解

Strong

Strong 修飾符表示指向并持有該對(duì)象,其修飾對(duì)象的引用計(jì)數(shù)會(huì)加1腥放。該對(duì)象只要引用計(jì)數(shù)不為0就不會(huì)被銷(xiāo)毀泛啸。當(dāng)然可以通過(guò)將變量強(qiáng)制賦值 nil 來(lái)進(jìn)行銷(xiāo)毀。

Weak

weak 修飾符指向但是并不持有該對(duì)象秃症,引用計(jì)數(shù)也不會(huì)加1平痰。在 Runtime 中對(duì)該屬性進(jìn)行了相關(guān)操作,無(wú)需處理伍纫,可以自動(dòng)銷(xiāo)毀。weak 用來(lái)修飾對(duì)象昂芜,多用于避免循環(huán)引用的地方莹规。weak 不可以修飾基本數(shù)據(jù)類(lèi)型。

assign

assign 主要用于修飾基本數(shù)據(jù)類(lèi)型泌神,
例如 NSInteger良漱,CGFloat,存儲(chǔ)在棧中欢际,內(nèi)存不用程序員管理母市。assign 是可以修飾對(duì)象的,但是會(huì)出現(xiàn)問(wèn)題损趋。

copy

copy 關(guān)鍵字和 strong 類(lèi)似患久,copy 多用于修飾有可變類(lèi)型的不可變對(duì)象 NSString, NSArray, NSDictionary上。

__unsafe_unretain

__unsafe_unretain 類(lèi)似于 weak 浑槽,但是當(dāng)對(duì)象被釋放后蒋失,指針依然保存著之前的地址,被釋放后的地址變?yōu)?僵尸對(duì)象桐玻,訪(fǎng)問(wèn)被釋放的地址就會(huì)出問(wèn)題篙挽,所以說(shuō)他是不安全的。

__autoreleasing

將對(duì)象賦值給附有 __autoreleasing 修飾的變量等同于 ARC 無(wú)效時(shí)調(diào)用對(duì)象的 autorelease 方法,實(shí)質(zhì)就是扔進(jìn)了自動(dòng)釋放池镊靴。

六铣卡、是否了解 深拷貝 和 淺拷貝 的概念链韭,集合類(lèi)深拷貝如何實(shí)現(xiàn)

簡(jiǎn)而言之:

1、對(duì)不可變的非集合對(duì)象煮落,copy是指針拷貝敞峭,mutablecopy是內(nèi)容拷貝

2、對(duì)于可變的非集合對(duì)象州邢,copy儡陨,mutablecopy都是內(nèi)容拷貝

3、對(duì)不可變的數(shù)組量淌、字典骗村、集合等集合類(lèi)對(duì)象,copy是指針拷貝呀枢,mutablecopy是內(nèi)容拷貝

4胚股、對(duì)于可變的數(shù)組、字典裙秋、集合等集合類(lèi)對(duì)象琅拌,copy,mutablecopy都是內(nèi)容拷貝

但是摘刑,對(duì)于集合對(duì)象的內(nèi)容復(fù)制僅僅是對(duì) 對(duì)象本身进宝,但是對(duì) 對(duì)象的里面的元素還是指針復(fù)制。要想復(fù)制整個(gè)集合對(duì)象枷恕,就要用集合深復(fù)制的方法党晋,有兩種:

(1)使用initWithArray:copyItems:方法,將第二個(gè)參數(shù)設(shè)置為YES即可

NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

(2)將集合對(duì)象進(jìn)行歸檔(archive)然后解歸檔(unarchive):

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

七徐块、 使用自動(dòng)引用計(jì)數(shù)應(yīng)遵循的原則

  • 不能使用 retain未玻、release、retainCount胡控、autorelease扳剿。
  • 不可以使用 NSAllocateObject庆聘、NSDeallocateObject踱蠢。
  • 必須遵守內(nèi)存管理方法的命名規(guī)則。
  • 不需要顯示的調(diào)用 Dealloc员凝。
  • 使用 @autoreleasePool 來(lái)代替 NSAutoreleasePool橙困。
  • 不可以使用區(qū)域 NSZone敛劝。
  • 對(duì)象性變量不可以作為 C 語(yǔ)言的結(jié)構(gòu)體成員。
  • 顯示轉(zhuǎn)換 id 和 void*纷宇。

八夸盟、 能不能簡(jiǎn)述一下 Dealloc 的實(shí)現(xiàn)機(jī)制

Dealloc 的實(shí)現(xiàn)機(jī)制是內(nèi)存管理部分的重點(diǎn),把這個(gè)知識(shí)點(diǎn)弄明白像捶,對(duì)于全方位的理解內(nèi)存管理的是很有必要上陕。

1.Dealloc 調(diào)用流程

  • 1.首先調(diào)用 _objc_rootDealloc()
  • 2.接下來(lái)調(diào)用 rootDealloc()
  • 3.這時(shí)候會(huì)判斷是否可以被釋放桩砰,判斷的依據(jù)主要有5個(gè),判斷是否有以下五種情況
    • NONPointer_ISA
    • weakly_reference
    • has_assoc
    • has_cxx_dtor
    • has_sidetable_rc
  • 4-1.如果有以上五中任意一種释簿,將會(huì)調(diào)用 object_dispose()方法亚隅,做下一步的處理。
  • 4-2.如果沒(méi)有之前五種情況的任意一種庶溶,則可以執(zhí)行釋放操作煮纵,C函數(shù)的 free()。
  • 5.執(zhí)行完畢偏螺。

2.object_dispose() 調(diào)用流程行疏。

  • 1.直接調(diào)用 objc_destructInstance()。
  • 2.之后調(diào)用 C函數(shù)的 free()套像。

3.objc_destructInstance() 調(diào)用流程

  • 1.先判斷 hasCxxDtor酿联,如果有 C++ 的相關(guān)內(nèi)容,要調(diào)用 object_cxxDestruct() 夺巩,銷(xiāo)毀 C++ 相關(guān)的內(nèi)容贞让。
  • 2.再判斷 hasAssocitatedObjects,如果有的話(huà)柳譬,要調(diào)用 object_remove_associations()喳张,銷(xiāo)毀關(guān)聯(lián)對(duì)象的一系列操作。
  • 3.然后調(diào)用 clearDeallocating()美澳。
  • 4.執(zhí)行完畢销部。

4.clearDeallocating() 調(diào)用流程。

  • 1.先執(zhí)行 sideTable_clearDellocating()人柿。
  • 2.再執(zhí)行 weak_clear_no_lock,在這一步驟中,會(huì)將指向該對(duì)象的弱引用指針置為 nil忙厌。
  • 3.接下來(lái)執(zhí)行 table.refcnts.eraser()凫岖,從引用計(jì)數(shù)表中擦除該對(duì)象的引用計(jì)數(shù)。
  • 4.至此為止逢净,Dealloc 的執(zhí)行流程結(jié)束哥放。

九、 內(nèi)存中的5大區(qū)分別是什么爹土?

  • 棧區(qū)(stack):由編譯器自動(dòng)分配釋放 甥雕,存放函數(shù)的參數(shù)值,局部變量的值等胀茵。其 操作方式類(lèi)似于數(shù)據(jù)結(jié)構(gòu)中的棧社露。

  • 堆區(qū)(heap):一般由程序員分配釋放, 若程序員不釋放琼娘,程序結(jié)束時(shí)可能由OS回收 峭弟。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事附鸽,分配方式倒是類(lèi)似于鏈表。

  • 全局區(qū)(靜態(tài)區(qū))(static):全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的瞒瘸,初始化的 全局變量和靜態(tài)變量在一塊區(qū)域坷备, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。 - 程序結(jié)束后由系統(tǒng)釋放情臭。

  • 文字常量區(qū):常量字符串就是放在這里的省撑。 程序結(jié)束后由系統(tǒng)釋放。

  • 程序代碼區(qū):存放函數(shù)體的二進(jìn)制代碼俯在。

十竟秫、 內(nèi)存管理默認(rèn)的關(guān)鍵字是什么?

  • MRC
@property (atomic,readWrite,retain) UIView *view;
  • ARC
@property (atomic,readWrite,strong) UIView *view;

如果改為基本數(shù)據(jù)類(lèi)型朝巫,那就是 assign鸿摇。

十一、內(nèi)存管理方案

  • 1.taggedPointer :存儲(chǔ)小對(duì)象如NSNumber劈猿。深入理解Tagged Pointer
  • 2.NONPOINTER_ISA(非指針型的isa):在64位架構(gòu)下拙吉,isa指針是占64比特位的,實(shí)際上只有30多位就已經(jīng)夠用了揪荣,為了提高利用率筷黔,剩余的比特位存儲(chǔ)了內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容。
  • 散列表:復(fù)雜的數(shù)據(jù)結(jié)構(gòu)仗颈,包括了引用計(jì)數(shù)表和弱引用表
    通過(guò)SideTables()結(jié)構(gòu)來(lái)實(shí)現(xiàn)的佛舱,SideTables()結(jié)構(gòu)下,有很多SideTable的數(shù)據(jù)結(jié)構(gòu)挨决。
    而sideTable當(dāng)中包含了自旋鎖请祖,引用計(jì)數(shù)表,弱引用表脖祈。
    SideTables()實(shí)際上是一個(gè)哈希表肆捕,通過(guò)對(duì)象的地址來(lái)計(jì)算該對(duì)象的引用計(jì)數(shù)在哪個(gè)sideTable中。

自旋鎖:

  • 自旋鎖是“忙等”的鎖盖高。
  • 適用于輕量訪(fǎng)問(wèn)慎陵。

引用計(jì)數(shù)表和弱引用表實(shí)際是一個(gè)哈希表,來(lái)提高查找效率喻奥。

十二席纽、內(nèi)存布局

  • 棧(stack):方法調(diào)用,局部變量等撞蚕,是連續(xù)的润梯,高地址往低地址擴(kuò)展
  • 堆(heap):通過(guò)alloc等分配的對(duì)象,是離散的,低地址往高地址擴(kuò)展仆救,需要我們手動(dòng)控制
  • 未初始化數(shù)據(jù)(bss):未初始化的全局變量等
  • 已初始化數(shù)據(jù)(data):已初始化的全局變量等
  • 代碼段(text):程序代碼

2抒和、64bit和32bit下 longchar*所占字節(jié)是不同的

  • char:1字節(jié)(ASCII 2 = 256個(gè)字符)
  • char*(即指針變量):4個(gè)字節(jié)(32位的尋址空間是2,即32個(gè)bit,也就是4個(gè)字節(jié)彤蔽。同理64位編譯器為8個(gè)字節(jié))
  • short int : 2個(gè)字節(jié) 范圍 -2~> 2 即 -32768~>32767
  • int: 4個(gè)字節(jié) 范圍 -2147483648~>2147483647
  • unsigned int : 4個(gè)字節(jié)
  • long: 4個(gè)字節(jié) 范圍 和int一樣 64位下8個(gè)字節(jié)摧莽,范圍 -9223372036854775808~9223372036854775807
  • long long: 8個(gè)字節(jié) 范圍-9223372036854775808~9223372036854775807
  • unsigned long long: 8個(gè)字節(jié) 最大值:1844674407370955161
  • float: 4個(gè)字節(jié)
  • double: 8個(gè)字節(jié)。

3顿痪、static镊辕、const 和 sizeof 關(guān)鍵字

static關(guān)鍵字

答:Static的用途主要有兩個(gè),一是用于修飾存儲(chǔ)類(lèi)型使之成為靜態(tài)存儲(chǔ)類(lèi)型蚁袭,二是用于修飾鏈接屬性使之成為內(nèi)部鏈接屬性征懈。

  • 1、靜態(tài)存儲(chǔ)類(lèi)型:

在函數(shù)內(nèi)定義的靜態(tài)局部變量揩悄,該變量存在內(nèi)存的靜態(tài)區(qū)卖哎,所以即使該函數(shù)運(yùn)行結(jié)束,靜態(tài)變量的值不會(huì)被銷(xiāo)毀删性,函數(shù)下次運(yùn)行時(shí)能仍用到這個(gè)值亏娜。

在函數(shù)外定義的靜態(tài)變量——靜態(tài)全局變量,該變量的作用域只能在定義該變量的文件中蹬挺,不能被其他文件通過(guò)extern引用维贺。

  • 2、內(nèi)部鏈接屬性

靜態(tài)函數(shù)只能在聲明它的源文件中使用巴帮。

const關(guān)鍵字

  • 1溯泣、聲明常變量,使得指定的變量不能被修改榕茧。
const int a = 5;/*a的值一直為5垃沦,不能被改變*/

const int b; b = 10;/*b的值被賦值為10后,不能被改變*/

const int *ptr; /*ptr為指向整型常量的指針用押,ptr的值可以修改肢簿,但不能修改其所指向的值*/

int *const ptr;/*ptr為指向整型的常量指針,ptr的值不能修改只恨,但可以修改其所指向的值*/

const int *const ptr;/*ptr為指向整型常量的常量指針译仗,ptr及其指向的值都不能修改*/
  • 2抬虽、修飾函數(shù)形參官觅,使得形參在函數(shù)內(nèi)不能被修改,表示輸入?yún)?shù)阐污。

int fun(const int a);或int fun(const char *str);
  • 3休涤、修飾函數(shù)返回值,使得函數(shù)的返回值不能被修改。
const char *getstr(void);使用:const *str= getstr();

const int getint(void);  使用:const int a =getint();

sizeof關(guān)鍵字

sizeof是在編譯階段處理功氨,且不能被編譯為機(jī)器碼序苏。sizeof的結(jié)果等于對(duì)象或類(lèi)型所占的內(nèi)存字節(jié)數(shù)。sizeof的返回值類(lèi)型為size_t捷凄。

  • 變量:int a; sizeof(a)為4忱详;

  • 指針:int *p; sizeof(p)為4;

  • 數(shù)組:int b[10]; sizeof(b)為數(shù)組的大小跺涤,4*10匈睁;int c[0]; sizeof(c)等于0

  • 結(jié)構(gòu)體:struct (int a; char ch;)s1; sizeof(s1)為8 與結(jié)構(gòu)體字節(jié)對(duì)齊有關(guān)。
    對(duì)結(jié)構(gòu)體求sizeof時(shí)桶错,有兩個(gè)原則:

(1)展開(kāi)后的結(jié)構(gòu)體的第一個(gè)成員的偏移量應(yīng)當(dāng)是被展開(kāi)的結(jié)構(gòu)體中最大的成員的整數(shù)倍航唆。

(2)結(jié)構(gòu)體大小必須是所有成員大小的整數(shù)倍,這里所有成員計(jì)算的是展開(kāi)后的成員院刁,而不是將嵌套的結(jié)構(gòu)體當(dāng)做一個(gè)整體糯钙。
  • 注意:不能對(duì)結(jié)構(gòu)體中的位域成員使用sizeof

  • sizeof(void)等于1

  • sizeof(void *)等于4

十三、 講一下 iOS 內(nèi)存管理的理解

實(shí)際上是三種方案的結(jié)合

  • 1.TaggedPointer(針對(duì)類(lèi)似于 NSNumber 的小對(duì)象類(lèi)型)

  • 2.NONPOINTER_ISA(64位系統(tǒng)下)

    • 第一位的 0 或 1 代表是純地址型 isa 指針退腥,還是 NONPOINTER_ISA 指針任岸。
    • 第二位,代表是否有關(guān)聯(lián)對(duì)象
    • 第三位代表是否有 C++ 代碼阅虫。
    • 接下來(lái)33位代表指向的內(nèi)存地址
    • 接下來(lái)有 弱引用 的標(biāo)記
    • 接下來(lái)有是否 delloc 的標(biāo)記....等等
  • 3.散列表(引用計(jì)數(shù)表演闭、weak表)

    • SideTables 表在 非嵌入式的64位系統(tǒng)中,有 64張 SideTable 表
    • 每一張 SideTable 主要是由三部分組成颓帝。自旋鎖米碰、引用計(jì)數(shù)表、弱引用表购城。
    • 全局的 引用計(jì)數(shù) 之所以不存在同一張表中吕座,是為了避免資源競(jìng)爭(zhēng),解決效率的問(wèn)題瘪板。
    • 引用計(jì)數(shù)表 中引入了 分離鎖 的概念吴趴,將一張表分拆成多個(gè)部分,對(duì)他們分別加鎖侮攀,可以實(shí)現(xiàn)并發(fā)操作锣枝,提升執(zhí)行效率

十四、 講一下 @dynamic 關(guān)鍵字兰英?

@dynamic 意味著編譯器不會(huì)幫助我們自動(dòng)合成 setter 和 getter 方法撇叁。我們需要手動(dòng)實(shí)現(xiàn)、這里就涉及到 Runtime 的動(dòng)態(tài)添加方法的知識(shí)點(diǎn)畦贸。

十五陨闹、 簡(jiǎn)要說(shuō)一下 @autoreleasePool 的數(shù)據(jù)結(jié)構(gòu)楞捂?

簡(jiǎn)單說(shuō)是雙向鏈表,每張鏈表頭尾相接趋厉,有 parent寨闹、child指針

每創(chuàng)建一個(gè)池子,會(huì)在首部創(chuàng)建一個(gè) 哨兵 對(duì)象,作為標(biāo)記

最外層池子的頂端會(huì)有一個(gè) next 指針君账。當(dāng)鏈表容量滿(mǎn)了繁堡,就會(huì)在鏈表的頂端,并指向下一張表乡数。

十六帖蔓、 訪(fǎng)問(wèn) __weak 修飾的變量,是否已經(jīng)被注冊(cè)在了 @autoreleasePool 中瞳脓?為什么塑娇?

答案是肯定的,__weak 修飾的變量屬于弱引用劫侧,如果沒(méi)有被注冊(cè)到 @autoreleasePool 中埋酬,創(chuàng)建之后也就會(huì)隨之銷(xiāo)毀,為了延長(zhǎng)它的生命周期烧栋,必須注冊(cè)到 @autoreleasePool 中写妥,以延緩釋放。

十七审姓、 retain珍特、release 的實(shí)現(xiàn)機(jī)制?

1.Retain 的實(shí)現(xiàn)機(jī)制魔吐。

SideTable& table = SideTables()[This];

size_t& refcntStorage = table.refcnts[This];

refcntStorage += SIZE_TABLE_RC_ONE;

2.Release 的實(shí)現(xiàn)機(jī)制扎筒。

SideTable& table = SideTables()[This];

size_t& refcntStorage = table.refcnts[This];

refcntStorage -= SIZE_TABLE_RC_ONE;

二者的實(shí)現(xiàn)機(jī)制類(lèi)似,概括講就是通過(guò)第一層 hash 算法酬姆,找到 指針變量 所對(duì)應(yīng)的 sideTable嗜桌。然后再通過(guò)一層 hash 算法,找到存儲(chǔ) 引用計(jì)數(shù) 的 size_t辞色,然后對(duì)其進(jìn)行增減操作骨宠。retainCount 不是固定的 1,SIZE_TABLE_RC_ONE 是一個(gè)宏定義相满,實(shí)際上是一個(gè)值為 4 的偏移量层亿。

十八、MRC(手動(dòng)引用計(jì)數(shù))和ARC(自動(dòng)引用計(jì)數(shù))

1立美、MRC:alloc匿又,retain,release悯辙,retainCount,autorelease,dealloc
2琳省、ARC:

  • ARC是LLVM和Runtime協(xié)作的結(jié)果
  • ARC禁止手動(dòng)調(diào)用retain,release躲撰,retainCount,autorelease關(guān)鍵字
  • ARC新增weak针贬,strong關(guān)鍵字

3、引用計(jì)數(shù)管理:

  • alloc: 經(jīng)過(guò)一系列函數(shù)調(diào)用拢蛋,最終調(diào)用了calloc函數(shù)桦他,這里并沒(méi)有設(shè)置引用計(jì)數(shù)為1
  • retain: 經(jīng)過(guò)兩次哈希查找,找到其對(duì)應(yīng)引用計(jì)數(shù)值谆棱,然后將引用計(jì)數(shù)加1(實(shí)際是加偏移量)
  • release:和retain相反快压,經(jīng)過(guò)兩次哈希查找,找到其對(duì)應(yīng)引用計(jì)數(shù)值垃瞧,然后將引用計(jì)數(shù)減1
  • dealloc:

4蔫劣、弱引用管理:

  • 添加weak變量:通過(guò)哈希算法位置查找添加。如果查找對(duì)應(yīng)位置中已經(jīng)有了當(dāng)前對(duì)象所對(duì)應(yīng)的弱引用數(shù)組个从,就把新的弱引用變量添加到數(shù)組當(dāng)中脉幢;如果沒(méi)有,就創(chuàng)建一個(gè)弱引用數(shù)組嗦锐,并將該弱引用變量添加到該數(shù)組中嫌松。

  • 當(dāng)一個(gè)被weak修飾的對(duì)象被釋放后,weak對(duì)象怎么處理的奕污?
    清除weak變量萎羔,同時(shí)設(shè)置指向?yàn)閚il。當(dāng)對(duì)象被dealloc釋放后碳默,在dealloc的內(nèi)部實(shí)現(xiàn)中贾陷,會(huì)調(diào)用弱引用清除的相關(guān)函數(shù),會(huì)根據(jù)當(dāng)前對(duì)象指針查找弱引用表嘱根,找到當(dāng)前對(duì)象所對(duì)應(yīng)的弱引用數(shù)組昵宇,將數(shù)組中的所有弱引用指針都置為nil。

5儿子、自動(dòng)釋放池:

在當(dāng)前runloop將要結(jié)束的時(shí)候調(diào)用objc_autoreleasePoolPop瓦哎,并push進(jìn)來(lái)一個(gè)新的AutoreleasePool

AutoreleasePoolPage是以棧為結(jié)點(diǎn)通過(guò)雙向鏈表的形式組合而成,是和線(xiàn)程一一對(duì)應(yīng)的柔逼。
內(nèi)部屬性有parent蒋譬,child對(duì)應(yīng)前后兩個(gè)結(jié)點(diǎn),thread對(duì)應(yīng)線(xiàn)程 愉适,next指針指向棧中下一個(gè)可填充的位置犯助。

  • AutoreleasePool實(shí)現(xiàn)原理?

編譯器會(huì)將 @autoreleasepool {} 改寫(xiě)為:

void * ctx = objc_autoreleasePoolPush;
    {}
objc_autoreleasePoolPop(ctx);
  • objc_autoreleasePoolPush:
    把當(dāng)前next位置置為nil维咸,即哨兵對(duì)象,然后next指針指向下一個(gè)可入棧位置剂买,
    AutoreleasePool的多層嵌套惠爽,即每次objc_autoreleasePoolPush,實(shí)際上是不斷地向棧中插入哨兵對(duì)象瞬哼。

  • objc_autoreleasePoolPop:
    根據(jù)傳入的哨兵對(duì)象找到對(duì)應(yīng)位置婚肆。
    給上次push操作之后添加的對(duì)象依次發(fā)送release消息。
    回退next指針到正確的位置坐慰。

十九较性、 BAD_ACCESS 在什么情況下出現(xiàn)?

訪(fǎng)問(wèn)了已經(jīng)被銷(xiāo)毀的內(nèi)存空間,就會(huì)報(bào)出這個(gè)錯(cuò)誤结胀。
根本原因是有 懸垂指針 沒(méi)有被釋放赞咙。

二十、 autoReleasePool 什么時(shí)候釋放?

App 啟動(dòng)后糟港,蘋(píng)果在主線(xiàn)程 RunLoop 里注冊(cè)了兩個(gè) Observer攀操,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)秸抚,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池崔赌。其 order 是 -2147483647,優(yōu)先級(jí)最高耸别,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前健芭。

第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池; Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池秀姐。這個(gè) Observer 的 order 是 2147483647慈迈,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后省有。

在主線(xiàn)程執(zhí)行的代碼痒留,通常是寫(xiě)在諸如事件回調(diào)、Timer回調(diào)內(nèi)的蠢沿。這些回調(diào)會(huì)被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著伸头,所以不會(huì)出現(xiàn)內(nèi)存泄漏,開(kāi)發(fā)者也不必顯示創(chuàng)建 Pool 了舷蟀。

二十一恤磷、 ARC自動(dòng)內(nèi)存管理的原則

  • 自己生成的對(duì)象,自己持有
  • 非自己生成的對(duì)象野宜,自己可以持有
  • 自己持有的對(duì)象不再需要時(shí)扫步,需要對(duì)其進(jìn)行釋放
  • 非自己持有的對(duì)象無(wú)法釋放

二十二、 ARC 在編譯時(shí)做了哪些工作

根據(jù)代碼執(zhí)行的上下文語(yǔ)境匈子,在適當(dāng)?shù)奈恢貌迦?retain河胎,release

二十三、 ARC 在運(yùn)行時(shí)做了哪些工作虎敦?

  • 主要是指 weak 關(guān)鍵字游岳。weak 修飾的變量能夠在引用計(jì)數(shù)為 0 時(shí)被自動(dòng)設(shè)置成 nil政敢,顯然是有運(yùn)行時(shí)邏輯在工作的。

  • 為了保證向后兼容性胚迫,ARC 在運(yùn)行時(shí)檢測(cè)到類(lèi)函數(shù)中的 autorelease 后緊跟其后 retain喷户,此時(shí)不直接調(diào)用對(duì)象的 autorelease 方法,而是改為調(diào)用 objc_autoreleaseReturnValue晌区。
    objc_autoreleaseReturnValue 會(huì)檢視當(dāng)前方法返回之后即將要執(zhí)行的那段代碼,若那段代碼要在返回對(duì)象上執(zhí)行 retain 操作通贞,則設(shè)置全局?jǐn)?shù)據(jù)結(jié)構(gòu)中的一個(gè)標(biāo)志位朗若,而不執(zhí)行 autorelease 操作,與之相似昌罩,如果方法返回了一個(gè)自動(dòng)釋放的對(duì)象哭懈,而調(diào)用方法的代碼要保留此對(duì)象,那么此時(shí)不直接執(zhí)行 retain 茎用,而是改為執(zhí)行 objc_retainAoutoreleasedReturnValue函數(shù)遣总。此函數(shù)要檢測(cè)剛才提到的標(biāo)志位,若已經(jīng)置位轨功,則不執(zhí)行 retain 操作旭斥,設(shè)置并檢測(cè)標(biāo)志位,要比調(diào)用 autorelease 和 retain 更快古涧。

二十四垂券、 ARC 的 retainCount 怎么存儲(chǔ)的?

存在64張哈希表中羡滑,根據(jù)哈希算法去查找所在的位置菇爪,無(wú)需遍歷,十分快捷

散列表(引用計(jì)數(shù)表柒昏、weak表)
- SideTables 表在 非嵌入式的64位系統(tǒng)中凳宙,有 64張 SideTable 表
- 每一張 SideTable 主要是由三部分組成。自旋鎖职祷、引用計(jì)數(shù)表氏涩、弱引用表。
- 全局的 引用計(jì)數(shù) 之所以不存在同一張表中有梆,是為了避免資源競(jìng)爭(zhēng)削葱,解決效率的問(wèn)題。
- 引用計(jì)數(shù)表 中引入了 分離鎖 的概念淳梦,將一張表分拆成多個(gè)部分析砸,對(duì)他們分別加鎖,可以實(shí)現(xiàn)并發(fā)操作爆袍,提升執(zhí)行效率

引用計(jì)數(shù)表(哈希表)

通過(guò)指針的地址首繁,查找到引用計(jì)數(shù)的地址作郭,大大提升查找效率

通過(guò) DisguisedPtr(objc_object) 函數(shù)存儲(chǔ),同時(shí)也通過(guò)這個(gè)函數(shù)查找弦疮,這樣就避免了循環(huán)遍歷夹攒。

二十五、 __weak 屬性修飾的變量胁塞,如何實(shí)現(xiàn)在變量沒(méi)有強(qiáng)引用后自動(dòng)置為 nil 咏尝?

用的弱引用 - weak表。也是一張 哈希表啸罢。

被 weak 修飾的指針變量所指向的地址是 key 编检,所有指向這塊內(nèi)存地址的指針會(huì)被添加在一個(gè)數(shù)組里,這個(gè)數(shù)組是 Value扰才。當(dāng)內(nèi)存地址銷(xiāo)毀允懂,數(shù)組里的所有對(duì)象被置為 nil。

二十六衩匣、 __weak 和 _Unsafe_Unretain 的區(qū)別蕾总?

weak 修飾的指針變量,在指向的內(nèi)存地址銷(xiāo)毀后琅捏,會(huì)在 Runtime 的機(jī)制下生百,自動(dòng)置為 nil。

_Unsafe_Unretain 不會(huì)置為 nil柄延,容易出現(xiàn) 懸垂指針置侍,發(fā)生崩潰。但是 _Unsafe_Unretain 比 __weak 效率高拦焚。

二十七蜡坊、 為什么已經(jīng)有了 ARC ,但還是需要 @AutoreleasePool 的存在?

避免內(nèi)存峰值赎败,及時(shí)釋放不需要的內(nèi)存空間

二十八秕衙、 函數(shù)返回一個(gè)對(duì)象時(shí),會(huì)對(duì)對(duì)象 autorelease 么僵刮?為什么据忘?

會(huì) ,為了延長(zhǎng)返回對(duì)象的生命周期搞糕,給其他使用者留足調(diào)用的時(shí)間

二十九勇吊、什么情況使用 weak 關(guān)鍵字,相比 assign 有什么不同?

  • 什么情況使用 weak 關(guān)鍵字?
    在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時(shí)候,往往要通過(guò)讓其中一端使用 weak 來(lái)解決,比如: delegate 代理屬性:自身已經(jīng)對(duì)它進(jìn)行一次強(qiáng)引用,沒(méi)有必要再?gòu)?qiáng)引用一次,此時(shí)也會(huì)使用 weak;自定義 IBOutlet 控件屬性一般也使用 weak窍仰;當(dāng)然汉规,也可以使用strong
  • 不同點(diǎn):
    weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值针史,也不釋放舊值晶伦。此特質(zhì)同assign類(lèi)似, 然而在屬性所指的對(duì)象遭到摧毀時(shí)啄枕,屬性值也會(huì)清空(nil out)婚陪。 而 assign 的“設(shè)置方法”只會(huì)執(zhí)行針對(duì)“純量類(lèi)型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡(jiǎn)單賦值操作频祝。
    assign 可以用于非 OC 對(duì)象,而 weak 必須用于 OC 對(duì)象

三十泌参、 如何讓自己的類(lèi)用 copy 修飾符?即讓自己寫(xiě)的對(duì)象具備拷貝功能?如何重寫(xiě)帶copy 關(guān)鍵字的 setter 常空?

  • 若想令自己所寫(xiě)的對(duì)象具有拷貝功能沽一,則需實(shí)現(xiàn) NSCopying 協(xié)議。如果自定義的對(duì)象分為可變版本與不可變版本窟绷,那么就要同時(shí)實(shí)現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議锯玛。
    • 具體步驟:

      • 需聲明該類(lèi)遵從 NSCopying 協(xié)議
      • 實(shí)現(xiàn) NSCopying 協(xié)議咐柜。該協(xié)議只有一個(gè)方法:
          - (id)copyWithZone:(NSZone *)zone;
      

      注意:一提到讓自己的類(lèi)用 copy 修飾符兼蜈,我們總是想覆寫(xiě)copy方法,其實(shí)真正需要實(shí)現(xiàn)的卻是 “copyWithZone” 方法拙友。

  • 重寫(xiě)帶 copy 關(guān)鍵字的 setter为狸,例如:
    - (void)setName:(NSString *)name {
        //[_name release];
        _name = [name copy];
    }    

三十一、@property的本質(zhì)是什么遗契?ivar辐棒、getter、setter是如何生成并添加到這個(gè)類(lèi)中的牍蜂?

  • @property 的本質(zhì)是實(shí)例變量(ivar)+存取方法(access method = getter + setter),即 @property = ivar + getter + setter;

“屬性” (property)作為 Objective-C 的一項(xiàng)特性漾根,主要的作用就在于封裝對(duì)象中的數(shù)據(jù)。 Objective-C 對(duì)象通常會(huì)把其所需要的數(shù)據(jù)保存為各種實(shí)例變量鲫竞。實(shí)例變量一般通過(guò)“存取方法”(access method)來(lái)訪(fǎng)問(wèn)辐怕。其中,“獲取方法” (getter)用于讀取變量值从绘,而“設(shè)置方法” (setter)用于寫(xiě)入變量值寄疏。

  • ivar、getter僵井、setter 是自動(dòng)合成這個(gè)類(lèi)中的

完成屬性定義后陕截,編譯器會(huì)自動(dòng)編寫(xiě)訪(fǎng)問(wèn)這些屬性所需的方法,此過(guò)程叫做“自動(dòng)合成”(autosynthesis)批什。需要強(qiáng)調(diào)的是农曲,這個(gè)過(guò)程由編譯器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼驻债。
除了生成方法代碼 getter朋蔫、setter 之外罚渐,編譯器還要自動(dòng)向類(lèi)中添加適當(dāng)類(lèi)型的實(shí)例變量,并且在屬性名前面加上下劃線(xiàn)驯妄,以此作為實(shí)例變量的名字荷并。在前例中,會(huì)生成兩個(gè)實(shí)例變量青扔,其名稱(chēng)分別為 _firstName 與 _lastName源织。也可以在類(lèi)的實(shí)現(xiàn)代碼里通過(guò) @synthesize 語(yǔ)法來(lái)指定實(shí)例變量的名字.

三十二、@protocol和category中如何使用@property?

  • 在 protocol 中使用 @property 只會(huì)生成 setter 和 getter 方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對(duì)象能實(shí)現(xiàn)該屬性
  • category 使用 @property 也是只會(huì)生成 setter 和 getter 方法的聲明,如果我們真的需要給 category 增加屬性的實(shí)現(xiàn),需要借助于運(yùn)行時(shí)的兩個(gè)函數(shù):objc_setAssociatedObject和objc_getAssociatedObject
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末微猖,一起剝皮案震驚了整個(gè)濱河市谈息,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凛剥,老刑警劉巖侠仇,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異犁珠,居然都是意外死亡逻炊,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)犁享,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)余素,“玉大人,你說(shuō)我怎么就攤上這事炊昆〗暗酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵凤巨,是天一觀(guān)的道長(zhǎng)视乐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)敢茁,這世上最難降的妖魔是什么佑淀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮卷要,結(jié)果婚禮上渣聚,老公的妹妹穿的比我還像新娘。我一直安慰自己僧叉,他們只是感情好奕枝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瓶堕,像睡著了一般隘道。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天谭梗,我揣著相機(jī)與錄音忘晤,去河邊找鬼。 笑死激捏,一個(gè)胖子當(dāng)著我的面吹牛设塔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播远舅,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼闰蛔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了图柏?” 一聲冷哼從身側(cè)響起序六,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蚤吹,沒(méi)想到半個(gè)月后例诀,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裁着,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年繁涂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跨算。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡爆土,死狀恐怖椭懊,靈堂內(nèi)的尸體忽然破棺而出诸蚕,到底是詐尸還是另有隱情,我是刑警寧澤氧猬,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布背犯,位于F島的核電站,受9級(jí)特大地震影響盅抚,放射性物質(zhì)發(fā)生泄漏漠魏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一妄均、第九天 我趴在偏房一處隱蔽的房頂上張望柱锹。 院中可真熱鬧,春花似錦丰包、人聲如沸禁熏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞧毙。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宙彪,已是汗流浹背矩动。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留释漆,地道東北人悲没。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像男图,于是被迫代替她去往敵國(guó)和親檀训。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容