iOS 底層探究一:alloc

這是我參與8月更文挑戰的第1天,活動詳情查看:8月更文挑戰

在咱們平常開發過程當中,對象初始化應該是咱們每一個人常常遇到的,可是對象初始化過程當中具體作了哪些事情,也許不少人不明因此,因此咱們來探究一下對象初始化的過程。c++

首先,咱們經過一個案例看一下緩存

2936157-11cca52c33527695.png

咱們建立了一個 LGPerson對象,對 p1alloc 操做,對 p2p3分別作 init 操做,而後分別打印 p1p2p3及它們的地址與指針的地址,而後咱們能夠看到p1p2p3的地址都同樣,可是他們的指針地址卻不同。能夠得出結論:markdown

1.LGPerson 類在執行完 alloc方法以後就開闢了內存空間,有了指針的指向。 2.p2p3p1指向的內存相同,說明 init 方法沒有對指針作操做。 3.在執行完init方法後咱們經過對p1p2p3指針取地址打印,能夠看到指針地址並不相同,且是以8個字節的間隔連續存儲在棧空間。函數

如今咱們想點進去看看 alloc 跟 init 方法的具體實現,咱們能夠看到都是隻有聲明沒有實現。這裏咱們對  alloc 方法打斷點,經過 Debug Workflow 生成彙編代碼能夠看到,這裏調用的是 objc_alloc 方法。post

allco 方法源碼調用流程

咱們知道底層調用的是objc_alloc方法,咱們下載objc的開源庫來看下具體的實現。ui

  1. [LGPerson alloc]

首先第一步在起始位置打上斷點。spa

2936157-62a35cb9859dc176.png

  1. [NSObject alloc] 第二步咱們看到來到了NSObject類的alloc方法,因爲全部類都是繼承於NSObject類。

2936157-7bd57b5d8cc8eafe.png

  1. objc_alloc

2936157-91e5ea73fbca5a44.png

這一步咱們會遇到一些問題,由於第二步命名調用的是 _objc_rootAlloc 放到,可是這裏卻來到了 objc_alloc 方法裏面。那麼爲何會走到這裏了呢?咱們在以下代碼中能夠找到答案。3d

2936157-29b48a20745c9ca1.png

  1. callAlloc

2936157-616efa267dbd8eb9.png

這裏能夠看到在 1931 行有個判斷,第一次調用的時候並無走到 1932 行,而是在 1940 行對 cls 也就是 LGPerson 類發送了 alloc 方法。指針

  1. _objc_rootAlloc

2936157-079849c29909015e.png

2936157-11f2fbf31ed1610d.png

這裏就會先進入 alloc 方法,而後調用 _objc_rootAlloc 方法。code

  1. _objc_rootAllocWithZone

2936157-d926b91e91dfa45b.png

如今就再次進入了 callAlloc 方法,這也是 callAlloc 方法調用兩次的緣由。此次會走到 1932 行,調用 _objc_rootAllocWithZone 方法。

  1. _class_createInstanceFromZone

2936157-96719736e889edbf.png

2936157-96719736e889edbf.png 在 _objc_rootAllocWithZone 方法以後會進入到  _class_createInstanceFromZone 方法,在這個方法裏面咱們着重關注幾個地方,在 7977 行這個會計算出 LGPerson 類相應的內存大小;7984 行會在堆區開闢出 size 大小的內存空間;7994 會綁定 isa 與 LGPerson 類相關聯, 並賦值了 hasCxxDtor c++ 相關的方法與函數;8002 行返回 obj 對象。

那麼問題來了,是如何知道該開闢多大的內存空間呢?具體咱們來看一下 instanceSize 方法。

**

inline size_t instanceSize(size_t extraBytes) const {
        // 若是有緩存會返回緩存的 size 大小
        if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
            return cache.fastInstanceSize(extraBytes);
        }
        
        // 若是沒有緩存,會經過 alignedInstanceSize() + extraBytes 這一步來計算 size 大小
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
    }
複製代碼

經過 instanceSize 方法咱們能夠看到,若是有緩存會返回緩存的 size 大小, 若是沒有就會經過 alignedInstanceSize() + extraBytes 這一步來計算 size 大小。開闢空間的大小與類的成員變量有關。繼承於 NSObject 的類都會有一個 isa 成員變量。若是當前類沒有其餘成員變量,返回的 size 大小就是 isa 的大小8 字節。可是由於 8 本身小於 16 字節,根據字節對齊原則,最後返回的是 16 字節。

**

@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
複製代碼

經過代碼也能夠看到,isa 是 Class 類型,objc_class 是一個結構體類型。isa 是結構體指針類型,因此是 8 個字節。objc_class 繼承與最原始的類型 objc_object

2936157-d75b1e91e8b8cd6e.png

最後咱們經過打印能夠看到對象內存的首字節就 isa 及當前對象的內存狀況。

allco 方法調用流程圖

能夠看到依次走入順序

objc_alloc → callAlloc → objc_msgSend → alloc → _objc_rootAlloc → callAlloc→ _objc_rootAllocWithZone

2936157-09cd490fa108a16f.png

相關文章
相關標籤/搜索