iOS-底層原理(24)-內(nèi)存管理之面試題

一使用CADisplayLink、NSTimer有什么注意點(diǎn)?
  • 循環(huán)引用

范例代碼

  • CADisplayLink
@property (strong, nonatomic) CADisplayLink *link;

// 1.發(fā)生內(nèi)存泄露
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

- (void)linkTest {
    NSLog(@"%s", __func__);
}
  • NSTimer
@property (strong, nonatomic) NSTimer *timer;

// 1.會(huì)內(nèi)存泄露
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

- (void)timerTest {
    NSLog(@"%s", __func__);
}
二 介紹下內(nèi)存的幾大區(qū)域
  • 代碼段:編譯之后的代碼

  • 數(shù)據(jù)段

    • 字符串常量:比如NSString *str = @"123"
    • 已初始化數(shù)據(jù):已初始化的全局變量、靜態(tài)變量等
    • 未初始化數(shù)據(jù):未初始化的全局變量、靜態(tài)變量等
  • 棧:函數(shù)調(diào)用開銷,比如局部變量。分配的內(nèi)存空間地址越來越小

  • 堆:通過alloc、malloc、calloc等動(dòng)態(tài)分配的空間,分配的內(nèi)存空間地址越來越大

三 講一下你對(duì) iOS 內(nèi)存管理的理解
四 ARC 都幫我們做了什么?
  • LLVM + Runtime

首先利用LLVM,幫我們自動(dòng)生成release,retain,autorelease代碼
需要runtime運(yùn)行時(shí)做一些事情
即ARC時(shí)LLVM編譯器和Runtime系統(tǒng)相互協(xié)作的一個(gè)結(jié)果

五 weak指針的實(shí)現(xiàn)原理

將弱引用存儲(chǔ)到一個(gè)哈希表里,當(dāng)對(duì)象要銷毀時(shí),就會(huì)取出當(dāng)前對(duì)象的弱引用表,將該表存儲(chǔ)的弱引用都給清除掉

六 autorelease對(duì)象在什么時(shí)機(jī)會(huì)被調(diào)用release

iOS在主線程的Runloop中注冊(cè)了2個(gè)Observer

  • 第1個(gè)Observer監(jiān)聽了kCFRunLoopEntry事件,會(huì)調(diào)用objc_autoreleasePoolPush()
  • 第2個(gè)Observer
    • 監(jiān)聽了kCFRunLoopBeforeWaiting事件,會(huì)調(diào)用objc_autoreleasePoolPop()objc_autoreleasePoolPush()
    • 監(jiān)聽了kCFRunLoopBeforeExit事件,會(huì)調(diào)用objc_autoreleasePoolPop()

代碼例子如下

- (void)viewDidLoad {
    [super viewDidLoad];

    Person *person = [[Person alloc] init];
    NSLog(@"%s", __func__);
}

這個(gè)Person什么時(shí)候調(diào)用release,是由RunLoop來控制的
它可能是在某次RunLoop循環(huán)中,RunLoop休眠之前調(diào)用了release
Person *person = [[[Person alloc] init] autorelease];

6.1 包含在@autoreleasepool中,則在pop的時(shí)候,即@@autoreleasepool作用域結(jié)束的時(shí)候銷毀。
七 方法里有局部對(duì)象, 出了方法后會(huì)立即釋放嗎
  • MRC環(huán)境下
    不一定,是在當(dāng)前runloop循環(huán)中,即將進(jìn)入休眠時(shí)釋放

  • ARC環(huán)境下

autorelease對(duì)象:在它所在的線程對(duì)應(yīng)的本次runloop即將進(jìn)入休眠時(shí)釋放

非autorelease對(duì)象:出了作用域就釋放

八 思考以下2段代碼能發(fā)生什么事?有什么區(qū)別?
@property(nonatomic,strong)NSString *name;
// @property(nonatomic,copy)NSString *name;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        // 加鎖
        self.name = [NSString stringWithFormat:@"abcdefghijk"];
        // 解鎖
    });
}

運(yùn)行結(jié)果

image.png
  • 分析

因?yàn)榻oself.name賦值,實(shí)際上是調(diào)用其set方法

- (void)setName:(NSString *)name {
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}

set方法內(nèi)部,會(huì)先執(zhí)行release操作,然后再執(zhí)行retain操作,如果是多個(gè)線程同時(shí)執(zhí)行set方法,則會(huì)造成釋放2次的情況,所有導(dǎo)致壞內(nèi)存訪問。

代碼二

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 1000; i++) {
    dispatch_async(queue, ^{
        self.name = [NSString stringWithFormat:@"abc"];
    });
}

執(zhí)行結(jié)果:正常訪問沒有奔潰報(bào)錯(cuò)。

  • 分析上面兩個(gè)為什么會(huì)出現(xiàn)不同的執(zhí)行結(jié)果
NSString *str1 = [NSString stringWithFormat:@"abc"];
NSString *str2 = [NSString stringWithFormat:@"123abc11111111"];

NSLog(@"%@ %@", [str1 class], [str2 class]);
NSLog(@"%p %p", str1,str2);

運(yùn)行結(jié)果

image.png

因?yàn)橐粋€(gè)是NSTaggedPointerString,一個(gè)是__NSCFString


項(xiàng)目連接地址- MemoryManage-CADisplayLinkNSTimer


本文參考MJ底層原理教程,非常感謝


  • 多多點(diǎn)贊,打賞更好,您的支持是我寫作的動(dòng)力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,641評(píng)論 1 32
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,224評(píng)論 8 265
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,652評(píng)論 18 399
  • 1.設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式,并簡(jiǎn)要敘述?設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型...
    龍飝閱讀 2,302評(píng)論 0 12
  • 你用動(dòng)作揭示許多懸念 那細(xì)膩而精致的手 在空中蝶樣飛舞 牽引我憂傷的眼睛 在無邊的默默黑夜里 我都無力阻止 別偽裝...
    MEIGUO27閱讀 338評(píng)論 0 1

友情鏈接更多精彩內(nèi)容