2019 iOS面試題-----內(nèi)存管理、自動釋放池與循環(huán)引用

2019 iOS面試題大全---全方面剖析面試
  • 內(nèi)存布局
  • 內(nèi)存管理方案
  • MRC(手動引用計數(shù))和ARC(自動引用計數(shù))
  • 循環(huán)引用

一、內(nèi)存布局

image.png
  • 棧(stack):方法調(diào)用甚垦,局部變量等届垫,是連續(xù)的释液,高地址往低地址擴展
  • 堆(heap):通過alloc等分配的對象,是離散的装处,低地址往高地址擴展误债,需要我們手動控制
  • 未初始化數(shù)據(jù)(bss):未初始化的全局變量等
  • 已初始化數(shù)據(jù)(data):已初始化的全局變量等
  • 代碼段(text):程序代碼
2、64bit和32bit下 long 和char*所占字節(jié)是不同的

char:1字節(jié)(ASCII 2^{8} = 256個字符)

char*(即指針變量): 4個字節(jié)(32位的尋址空間是2^{32},即32個bit妄迁,也就是4個字節(jié)寝蹈。同理64位編譯器為8個字節(jié))

short int : 2個字節(jié) 范圍 -2^{16}~> 2^{16} 即 -32768~>32767

int: 4個字節(jié) 范圍 -2147483648~>2147483647

unsigned int : 4個字節(jié)

long: 4個字節(jié) 范圍 和int一樣 64位下8個字節(jié),范圍 -9223372036854775808~9223372036854775807

long long: 8個字節(jié) 范圍 -9223372036854775808~9223372036854775807

unsigned long long: 8個字節(jié) 最大值:1844674407370955161

float: 4個字節(jié)

double: 8個字節(jié)登淘。

3箫老、static、const和sizeof關(guān)鍵字
static關(guān)鍵字

答:Static的用途主要有兩個黔州,一是用于修飾存儲類型使之成為靜態(tài)存儲類型耍鬓,二是用于修飾鏈接屬性使之成為內(nèi)部鏈接屬性。

  • 1流妻、靜態(tài)存儲類型:

在函數(shù)內(nèi)定義的靜態(tài)局部變量牲蜀,該變量存在內(nèi)存的靜態(tài)區(qū),所以即使該函數(shù)運行結(jié)束绅这,靜態(tài)變量的值不會被銷毀涣达,函數(shù)下次運行時能仍用到這個值。

在函數(shù)外定義的靜態(tài)變量——靜態(tài)全局變量证薇,該變量的作用域只能在定義該變量的文件中度苔,不能被其他文件通過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是在編譯階段處理踩验,且不能被編譯為機器碼鸥诽。sizeof的結(jié)果等于對象或類型所占的內(nèi)存字節(jié)數(shù)。sizeof的返回值類型為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é)對齊有關(guān)。
    對結(jié)構(gòu)體求sizeof時扁远,有兩個原則:

     (1)展開后的結(jié)構(gòu)體的第一個成員的偏移量應(yīng)當(dāng)是被展開的結(jié)構(gòu)體中最大的成員的整數(shù)倍俊鱼。
    
     (2)結(jié)構(gòu)體大小必須是所有成員大小的整數(shù)倍刻像,這里所有成員計算的是展開后的成員畅买,而不是將嵌套的結(jié)構(gòu)體當(dāng)做一個整體。
    
  • 注意:不能對結(jié)構(gòu)體中的位域成員使用sizeof

    sizeof(void)等于1

    sizeof(void *)等于4

二细睡、內(nèi)存管理方案

  • taggedPointer :存儲小對象如NSNumber谷羞。深入理解Tagged Pointer
  • NONPOINTER_ISA(非指針型的isa):在64位架構(gòu)下,isa指針是占64比特位的溜徙,實際上只有30多位就已經(jīng)夠用了湃缎,為了提高利用率,剩余的比特位存儲了內(nèi)存管理的相關(guān)數(shù)據(jù)內(nèi)容蠢壹。
  • 散列表:復(fù)雜的數(shù)據(jù)結(jié)構(gòu)嗓违,包括了引用計數(shù)表和弱引用表
    通過SideTables()結(jié)構(gòu)來實現(xiàn)的,SideTables()結(jié)構(gòu)下图贸,有很多SideTable的數(shù)據(jù)結(jié)構(gòu)蹂季。
    而sideTable當(dāng)中包含了自旋鎖,引用計數(shù)表疏日,弱引用表偿洁。
    SideTables()實際上是一個哈希表,通過對象的地址來計算該對象的引用計數(shù)在哪個sideTable中沟优。

自旋鎖:

  • 自旋鎖是“忙等”的鎖涕滋。
  • 適用于輕量訪問。

引用計數(shù)表和弱引用表實際是一個哈希表挠阁,來提高查找效率宾肺。

三溯饵、MRC(手動引用計數(shù))和ARC(自動引用計數(shù))

1、MRC:alloc锨用,retain瓣喊,release,retainCount,autorelease,dealloc
2黔酥、ARC:
  • ARC是LLVM和Runtime協(xié)作的結(jié)果
  • ARC禁止手動調(diào)用retain藻三,release,retainCount,autorelease關(guān)鍵字
  • ARC新增weak跪者,strong關(guān)鍵字
3棵帽、引用計數(shù)管理:
  • alloc: 經(jīng)過一系列函數(shù)調(diào)用,最終調(diào)用了calloc函數(shù)渣玲,這里并沒有設(shè)置引用計數(shù)為1
  • retain: 經(jīng)過兩次哈希查找逗概,找到其對應(yīng)引用計數(shù)值,然后將引用計數(shù)加1(實際是加偏移量)
  • release:和retain相反忘衍,經(jīng)過兩次哈希查找逾苫,找到其對應(yīng)引用計數(shù)值,然后將引用計數(shù)減1
  • dealloc:
    image.png
4枚钓、弱引用管理:
  • 添加weak變量:通過哈希算法位置查找添加铅搓。如果查找對應(yīng)位置中已經(jīng)有了當(dāng)前對象所對應(yīng)的弱引用數(shù)組,就把新的弱引用變量添加到數(shù)組當(dāng)中搀捷;如果沒有星掰,就創(chuàng)建一個弱引用數(shù)組,并將該弱引用變量添加到該數(shù)組中嫩舟。
  • 當(dāng)一個被weak修飾的對象被釋放后氢烘,weak對象怎么處理的?
    清除weak變量家厌,同時設(shè)置指向為nil播玖。當(dāng)對象被dealloc釋放后,在dealloc的內(nèi)部實現(xiàn)中饭于,會調(diào)用弱引用清除的相關(guān)函數(shù)蜀踏,會根據(jù)當(dāng)前對象指針查找弱引用表,找到當(dāng)前對象所對應(yīng)的弱引用數(shù)組镰绎,將數(shù)組中的所有弱引用指針都置為nil脓斩。
5、自動釋放池:

在當(dāng)次runloop將要結(jié)束的時候調(diào)用objc_autoreleasePoolPop畴栖,并push進來一個新的AutoreleasePool

AutoreleasePoolPage是以棧為結(jié)點通過雙向鏈表的形式組合而成随静,是和線程一一對應(yīng)的。
內(nèi)部屬性有parent,child對應(yīng)前后兩個結(jié)點燎猛,thread對應(yīng)線程 恋捆,next指針指向棧中下一個可填充的位置。

  • AutoreleasePool實現(xiàn)原理重绷?

編譯器會將 @autoreleasepool {} 改寫為:

void * ctx = objc_autoreleasePoolPush;
    {}
objc_autoreleasePoolPop(ctx);
  • objc_autoreleasePoolPush:
    把當(dāng)前next位置置為nil沸停,即哨兵對象,然后next指針指向下一個可入棧位置,
    AutoreleasePool的多層嵌套昭卓,即每次objc_autoreleasePoolPush愤钾,實際上是不斷地向棧中插入哨兵對象。
  • objc_autoreleasePoolPop:
    根據(jù)傳入的哨兵對象找到對應(yīng)位置候醒。
    給上次push操作之后添加的對象依次發(fā)送release消息能颁。
    回退next指針到正確的位置。

四倒淫、循環(huán)引用

循環(huán)引用的實質(zhì):多個對象相互之間有強引用伙菊,不能釋放讓系統(tǒng)回收。
如何解決循環(huán)引用敌土?

1镜硕、避免產(chǎn)生循環(huán)引用,通常是將 strong 引用改為 weak 引用返干。
比如在修飾屬性時用weak
在block內(nèi)調(diào)用對象方法時兴枯,使用其弱引用,這里可以使用兩個宏


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

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

還可以使用__block來修飾變量
在MRC下犬金,__block不會增加其引用計數(shù)念恍,避免了循環(huán)引用
在ARC下,__block修飾對象會被強引用晚顷,無法避免循環(huán)引用,需要手動解除疗疟。

2该默、在合適時機去手動斷開循環(huán)引用。
通常我們使用第一種策彤。

循環(huán)引用場景:
  • 自循環(huán)引用
    對象強持有的屬性同時持有該對象
  • 相互循環(huán)引用


    image.png
  • 多循環(huán)引用


    image.png
1栓袖、代理(delegate)循環(huán)引用屬于相互循環(huán)引用

delegate 是iOS中開發(fā)中比較常遇到的循環(huán)引用,一般在聲明delegate的時候都要使用弱引用 weak,或者assign,當(dāng)然怎么選擇使用assign還是weak店诗,MRC的話只能用assign裹刮,在ARC的情況下最好使用weak,因為weak修飾的變量在釋放后自動指向nil庞瘸,防止野指針存在

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

在控制器內(nèi),創(chuàng)建NSTimer作為其屬性,由于定時器創(chuàng)建后也會強引用該控制器對象违霞,那么該對象和定時器就相互循環(huán)引用了嘴办。
如何解決呢?
這里我們可以使用手動斷開循環(huán)引用:
如果是不重復(fù)定時器买鸽,在回調(diào)方法里將定時器invalidate并置為nil即可涧郊。
如果是重復(fù)定時器,在合適的位置將其invalidate并置為nil即可

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

一個簡單的例子:

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

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

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

__weak typeof(self) weakSelf = self;

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

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

還有一種場景,在block執(zhí)行開始時self對象還未被釋放桌吃,而執(zhí)行過程中朱沃,self被釋放了,由于是用weak修飾的茅诱,那么weakSelf也被釋放了逗物,此時在block里訪問weakSelf時,就可能會發(fā)生錯誤(向nil對象發(fā)消息并不會崩潰瑟俭,但也沒任何效果)翎卓。
對于這種場景,應(yīng)該在block中對 對象使用__strong修飾摆寄,使得在block期間對 對象持有失暴,block執(zhí)行結(jié)束后,解除其持有微饥。

__weak typeof(self) weakSelf = self;

self.myBlock = ^() {

        __strong __typeof(self) strongSelf = weakSelf;

        [strongSelf test];
 };

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逗扒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子欠橘,更是在濱河造成了極大的恐慌矩肩,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肃续,死亡現(xiàn)場離奇詭異黍檩,居然都是意外死亡,警方通過查閱死者的電腦和手機始锚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門刽酱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞧捌,你說我怎么就攤上這事棵里。” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵衍慎,是天一觀的道長转唉。 經(jīng)常有香客問我,道長稳捆,這世上最難降的妖魔是什么赠法? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮乔夯,結(jié)果婚禮上砖织,老公的妹妹穿的比我還像新娘。我一直安慰自己末荐,他們只是感情好侧纯,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甲脏,像睡著了一般眶熬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上块请,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天娜氏,我揣著相機與錄音,去河邊找鬼墩新。 笑死贸弥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的海渊。 我是一名探鬼主播绵疲,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼臣疑!你這毒婦竟也來了盔憨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤朝捆,失蹤者是張志新(化名)和其女友劉穎般渡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芙盘,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年脸秽,在試婚紗的時候發(fā)現(xiàn)自己被綠了儒老。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡记餐,死狀恐怖驮樊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤囚衔,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布挖腰,位于F島的核電站,受9級特大地震影響练湿,放射性物質(zhì)發(fā)生泄漏猴仑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一肥哎、第九天 我趴在偏房一處隱蔽的房頂上張望辽俗。 院中可真熱鬧,春花似錦篡诽、人聲如沸崖飘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朱浴。三九已至,卻和暖如春达椰,著一層夾襖步出監(jiān)牢的瞬間翰蠢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迷守,地道東北人侥钳。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像趁尼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猖辫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345