OC底層基礎:內(nèi)存管理

一、內(nèi)存布局

  • 代碼段:編譯之后的代碼
  • 數(shù)據(jù)段
    1. 字符串常量:比如NSString *str = @"123"
    2. 已初始化數(shù)據(jù):已初始化的全局變量榛鼎、靜態(tài)變量等
    3. 未初始化數(shù)據(jù):未初始化的全局變量硅瞧、靜態(tài)變量等
  • 棧: 函數(shù)調用開銷朋譬,比如局部變量笨奠。分配的內(nèi)存空間地址越來越小
  • 堆:通過allocmalloc淮蜈、calloc等動態(tài)分配的空間,分配的內(nèi)存空間地址越來越大
截屏2022-03-15 下午5.25.58.png
/**
     字符串常量
     str=0x10dfa0068
     
     已初始化的全局變量已卷、靜態(tài)變量
     &a =0x10dfa0db8
     &c =0x10dfa0dbc
     
     未初始化的全局變量梧田、靜態(tài)變量
     &d =0x10dfa0e80
     &b =0x10dfa0e84
     
     堆
     objc=0x608000012210
     
     棧
     &f =0x7ffee1c60fe0
     & = 0x7ffee1c60fe4
     */

二、自動釋放池

  • 自動釋放池的主要底層數(shù)據(jù)結構是:__AtAutoreleasePool侧蘸、AutoreleasePoolPage
  • 調用了autorelease的對象最終都是通過AutoreleasePoolPage對象來管理的
  • 源碼分析
    1. clang重寫@autoreleasepool
    2. objc4源碼:NSObject.mm
    class AutoreleasePoolPage;
    struct AutoreleasePoolPageData
    {
        magic_t const magic;
        __unsafe_unretained id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;
    };
    

AutoreleasePoolPage的結構

  • 每個AutoreleasePoolPage對象占用4096字節(jié)內(nèi)存裁眯,除了用來存放它內(nèi)部的成員變量,剩下的空間用來存放autorelease對象的地址
  • 所有AutoreleasePoolPage`對象通過雙向鏈表的形式連接在一起
AutoreleasePoolPage的結構.png
  • 調用push方法會將一個POOL_BOUNDARY入棧讳癌,并且返回其存放的內(nèi)存地址
  • 調用pop方法時傳入一個POOL_BOUNDARY的內(nèi)存地址未状,會從最后一個入棧的對象開始發(fā)送release消息,直到遇到這個POOL_BOUNDARY
  • id *next指向了下一個能存放autorelease對象地址的區(qū)域

Runloop和Autorelease

  • iOS在主線程的Runloop中注冊了2個Observer
    1. 第1個Observer監(jiān)聽了kCFRunLoopEntry事件析桥,會調用objc_autoreleasePoolPush()
    2. 第2個Observer
      監(jiān)聽了kCFRunLoopBeforeWaiting事件司草,會調用objc_autoreleasePoolPop()objc_autoreleasePoolPush()
      監(jiān)聽了kCFRunLoopBeforeExit事件泡仗,會調用objc_autoreleasePoolPop()

三埋虹、CADisplayLink、NSTimer使用注意

  • CADisplayLink娩怎、NSTimer會對target產(chǎn)生強引用搔课,如果target又對它們產(chǎn)生強引用,那么會引發(fā)循環(huán)引用
  • 解決方案
    1. 使用block
     __weak typeof(self) weakSelf = self;
     [NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
         [weakSelf test];
     }];
    
    1. 使用代理對象(NSProxy
    + (instancetype)proxyWithTarget:(id)target {
      TimerProxy *proxy = [self alloc];
      proxy.target = target;
      return proxy;
    }
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
      return [self.target methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
      [anInvocation invokeWithTarget:self.target];
    }
    
    TimerProxy *proxy = [TimerProxy proxyWithTarget:self];
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:proxy selector:@selector(test) userInfo:nil repeats:YES];
    

四截亦、GCD定時器

  • NSTimer依賴于RunLoop爬泥,如果RunLoop的任務過于繁重柬讨,可能會導致NSTimer不準時
  • GCD的定時器會更加準時
   //創(chuàng)建隊列
   dispatch_queue_t queue = dispatch_get_main_queue();
   //1.創(chuàng)建GCD中的定時器
   /*
     第一個參數(shù):創(chuàng)建source的類型 DISPATCH_SOURCE_TYPE_TIMER:定時器
     第二個參數(shù):0
     第三個參數(shù):0
     第四個參數(shù):隊列
   */
  dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

   //2.設置時間等
   /*
     第一個參數(shù):定時器對象
     第二個參數(shù):DISPATCH_TIME_NOW 表示從現(xiàn)在開始計時
     第三個參數(shù):間隔時間 GCD里面的時間最小單位為 納秒
     第四個參數(shù):精準度(表示允許的誤差,0表示絕對精準)
   */
  dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

   //3.要調用的任務
  dispatch_source_set_event_handler(timer, ^{
      NSLog(@"GCD-----%@",[NSThread currentThread]);
  });

  //4.開始執(zhí)行
  dispatch_resume(timer);

五、Tagged pointer

  • 從64bit開始袍啡,iOS引入了Tagged pointer技術踩官,用于優(yōu)化NSNumberNSDate境输、NSString等小對象的存儲
  • 在沒有使用Tagged pointer之前蔗牡,NSNumber等對象需要動態(tài)分配內(nèi)存、維護引用計數(shù)等嗅剖,NSNumber指針存儲的是堆中NSNumber對象的地址值
  • 使用Tagged pointer之后辩越,NSNumber指針里面存儲的數(shù)據(jù)變成:Tag + Data,也就是將數(shù)據(jù)直接存儲在了指針中
  • 當指針不夠存儲數(shù)據(jù)時信粮,才會使用動態(tài)分配內(nèi)存的方式來存儲數(shù)據(jù)
  • objc_msgSend能識別Tagged pointer黔攒,比如NSNumberintValue方法,直接從指針提取數(shù)據(jù)强缘,節(jié)省了以前的調用開銷
  • 如何判斷一個指針是否為Tagged pointer
    1. iOS平臺亏钩,最高有效位是1(第64bit)
    2. Mac平臺,最低有效位是1
  • 判斷是否為Tagged Pointer
static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}
#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define OBJC_MSB_TAGGED_POINTERS 0
#else
    // Everything else - tag bit is MSB
#   define OBJC_MSB_TAGGED_POINTERS 1
#endif

#if OBJC_MSB_TAGGED_POINTERS
#   define _OBJC_TAG_MASK (1UL<<63)
#else
#   define _OBJC_TAG_MASK 1UL
#endif

注:面試題

  • 思考以下兩端代碼能發(fā)生什么事欺旧,有什么區(qū)別
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"];
        });
    }

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"];
        });
    }

注:可能涉及的面試題

  • 使用CADisplayLink姑丑、NSTimer有什么注意點
  • 介紹下內(nèi)存的幾大區(qū)域
  • 講一下你對iOS內(nèi)存管理的理解
  • autorelease在什么時機會被釋放
  • 方法里有局部對象,出了方法后會立即釋放嗎
  • ARC都幫我們做了什么
  • weak指針的實現(xiàn)原理

上一篇:
OC底層基礎:多線程GCD
下一篇:
[OC底層基礎]

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末辞友,一起剝皮案震驚了整個濱河市栅哀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌称龙,老刑警劉巖留拾,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲫尊,居然都是意外死亡痴柔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門疫向,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咳蔚,“玉大人,你說我怎么就攤上這事搔驼。” “怎么了糯耍?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵温技,是天一觀的道長。 經(jīng)常有香客問我舵鳞,道長系任,這世上最難降的妖魔是什么虐块? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮霜旧,結果婚禮上儡率,老公的妹妹穿的比我還像新娘儿普。我一直安慰自己,他們只是感情好个绍,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布浪汪。 她就那樣靜靜地躺著死遭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钉迷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天篷牌,我揣著相機與錄音枷颊,去河邊找鬼夭苗。 笑死,一個胖子當著我的面吹牛题造,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丢习,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼咐低,長吁一口氣:“原來是場噩夢啊……” “哼袜腥!你這毒婦竟也來了?” 一聲冷哼從身側響起鲤屡,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤酒来,失蹤者是張志新(化名)和其女友劉穎堰汉,沒想到半個月后显拜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡矮固,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年档址,在試婚紗的時候發(fā)現(xiàn)自己被綠了邻梆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浦妄。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡见芹,死狀恐怖蠢涝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徘铝,我是刑警寧澤惯吕,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布废登,位于F島的核電站,受9級特大地震影響揭北,放射性物質發(fā)生泄漏吏颖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劝术。 院中可真熱鬧养晋,春花似錦、人聲如沸绳泉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孝凌。三九已至月腋,卻和暖如春瓣赂,著一層夾襖步出監(jiān)牢的瞬間钩述,已是汗流浹背牙勘。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工方面, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留色徘,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓横腿,卻偏偏與公主長得像斤寂,于是被迫代替她去往敵國和親遍搞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

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