1.block

首先,我们了解下闭包的概念,闭包就是能够读取其他函数内部变量的函数,在很多语言上都有类似的实现,而block可以说就是OC上对闭包的实现,block实际上就是把代码块指向函数指针,并且能截获变量。

block的使用代码如下:

^{
    NSLog(@"Hello");
};

可以看到代码被^{}包起来了,其实^{}只是个语言标记,clang编译器会将其转换为block结构体。

2.内存结构

block目前最新的结构体定义如下:

struct Block_layout {
    void *isa;  // initialized to &__NSGlobalBlock__ or &__NSMallocBlock__ or &__NSStackBlock__ 
    volatile int32_t flags; // contains ref count
    int32_t reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    void (*copy)(void *dst, const void *src);
    void (*dispose)(const void *);
};

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};

可以看到block也具有isa指针,指向__NSGlobalBlock__/__NSMallocBlock__/__NSStackBlock__ 所对应的地址,flags为对应的flag数值,有很多作用,invoke函数指针指向相应的代码块,第一个参数为block本身的指针。这里主要就是isa,他代表该block所对应的类型。

__NSGlobalBlock__:当Block中没有引用外部变量,或引用了全局变量,const 变量或static变量时为该类型,存放在data段里。

__NSMallocBlock__:当Block引用了外部的OC对象,Block对象或用__block修饰的变量时则为该类型,存放在堆里,进行copy操作的话,会增加引用计数。

__NSStackBlock__:当Block中使用了外部栈变量,则为该类型,存放在栈里,在ARC下,block赋值后会被__strong修饰,然后走objc_retainBlock->_Block_copy->_Block_copy_interna流程,被移到__NSMallocBlock__里,只有__weak声明的block才在栈里。

可以通过[block class]来获取到该block的类型。

3.截获变量

理解Block的关键,在于理解Block是如何处理外部变量的,Block中会截取这些类型的外部变量:

1.全局/静态变量

对于全局/静态变量,Block会直接引用这类变量,不会copy。

static int a = 13;
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"Outside Block, static int a address is %p", &a);
   ^{
        NSLog(@"Inside Block, static int a address is %p", &a);
    }();
}

这里打印出来的地址都是同一个,全局变量因为作用域的问题,在block内直接访问修改是没问题的,且不会保存到block的结构体内,而静态变量因为内存地址不变,所以传递到block内用的是指针地址,所以也可以直接访问和修改。

2.自动(auto)存储类型/局部变量

对于auto类型的变量,(Block类型、__block、NSObject类型除外),block内部是使用const copy了一份,并存放到block结构体内比如下面:

int b = 12;
    NSLog(@"Outside Block, address of int b is %p", &b);
   ^{
        NSLog(@"Inside Block, address of int b is %p", &b);
    }();

这里打印的是不同地址,block内部结构是:

struct __block_literal_2 {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(struct __block_literal_2 *);
    struct __block_descriptor_2 *descriptor;
    const int ;   // 这里会有一份const copy
};

一般的,对于标量类型(int, float, bool等基本类型),struct,unions和函数指针类型,都会采用const copy的方式,将Block外部的变量拷贝到Block内部。

3.对象类型(Block/NSObject)

截取NSObject对象时,同样会做一份const copy,下面代码:

NSObject *obj = [NSObject new];
NSLog(@"Outside Block, address:%p-----instance:%@", &obj,obj);
^{
    NSLog(@"Inside Block, address:%p-----instance:%@", &obj,obj);
}();

打印了:
Outside Block, address:0x16f445368-----instance:<NSObject: 0x283ec45a0>
Inside Block, address:0x16f445360-----instance:<NSObject: 0x283ec45a0>

说明block只copy了指针,并没有copy对象,只是浅拷贝。这里有个细节,如何是修改对象的话,会直接报错,当是修改对象内部的成员变量是没问题的,因为修改成员变量并不会影响对象本身的内存地址,而且因为是浅拷贝,所以修改的值出了block也是生效的。比如下面这代码:

NSMutableString *str = [NSMutableString stringWithString:@"Hello"];
blockType blk = ^{
    [str appendString:@" World"];
};
blk();
NSLog(@"Now str is %@", str);

是可以正常打印Hello World的,因为appendString并不会改变str指针本身,只是修改了指针指向的内容。

4.__block修饰的变量

这里最复杂的就是__block修饰的变量,除了全局变量和静态变量外,当我们想在block内修改变量的值时,需要使用__block修饰。看下如下代码:

    __block int b = 13;
    NSLog(@"Outside Block, address of __block int b is %p, b = %d", &b, b);
    blockType1 blk = ^{
        b++;
        NSLog(@"Inside Block, address of __block int b is %p, b = %d", &b, b);
    };
    blk();
    NSLog(@"After Block, address of __block int b is %p, b = %d", &b, b);

这里变量b在进入block后,指针变了,这里实际上被转换成_block_byref_i结构了,比如:

__block int  i = 10;
i = 11;

使用clang改写变成了:

struct _block_byref_i {
  void *isa;
  struct _block_byref_i *forwarding;
  int flags;   //refcount;
  int size;
  int captured_i;
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };

i.forwarding->captured_i = 11;

可以看到,__block int i被改写为了struct _block_byref_i 结构体,这里关键属性就是forwardingforwarding指向一个block结构体,当block在栈上时,forwarding指向的是自身block,当blockcopy到堆上时,forwarding指向的是堆上的block地址,而堆上的block的forwarding指向自身。如下图所示:

所以前面的变量b才会出现指针地址被改变的问题。

4.用途

block有以下几种用法:

1.作为属性

@property (nonatomic, copy) void(^block)(void);

这里block使用了copy来修饰,其实在ARC下,使用strongcopy效果是一样的,底层实现都是走_Block_copy方法,创建后的block__NSMallocBlock__类型的。

如果使用weak修饰的话,创建后的block__NSStackBlock__类型的,但是就有可能被栈释放,所以我们一般用strong或者copy

另外如果该block的实现里没有捕获外部变量或者捕获了全局变量,静态变量,那么类型会优先变成__NSGlobalBlock__,不受修饰影响。

2.作为参数

- (void)testBlock:(void(^)(void))block {

}

//1.传入成员属性
[self testBlock:self.block];
//2.传入临时创建的block,并捕获外部变量
[self testBlock:^{
    NSLog(@"%@",num);
}];
//3.传入临时创建的block,不捕获外部变量
[self testBlock:^{

}];

在上面的3中调用方式中,第一种传入成员属性,因为传的是指针地址,所以该block等同于self.block的内存分布。

第二种传入临时创建的block,该block因为捕获了外部变量,如果该变量非全局或静态变量,则该block__NSStackBlock__类型。

第三种因为没有捕获任何外部变量,所以该block__NSGlobalBlock__类型,生命周期等同于app,也就是会一直常驻内存。

3.作为返回值

//1.返回成员属性
- (void(^)(void))returnBlock {
    return self.block;
}
//2.返回临时创建的block,并捕获外部变量
- (void(^)(void))returnBlock {
    NSNumber *num = @0;
    return ^{
        NSLog(@"%@",num);
    };
}
//3.返回传入临时创建的block,不捕获外部变量
- (void(^)(void))returnBlock {
    NSNumber *num = @0;
    return ^{

    };
}

以上3种返回值,其block的内存分布基本与作为参数时相关,唯一不同的是,第二种block引用了外部变量,本来是__NSStackBlock__类型的,但是因为需要return,会被__strong修饰,所以变成了__NSMallocBlock__

5.循环引用

当对象内部的成员变量引用了block,而block又引用了对象时,会导致循环引用。
下面几种情况都会造成循环引用:

这里self->self.strBlk->self.array->self,是最常见的循环引用,能被leak和xcode检测出来,当是如果这里多嵌套几层self->self.ival->self.strBlk->self.array->self,也会导致循环引用,但是leak会检测不出来,xcode也不会报警。

上面这种循环引用就比较难发现了,这里在MRC下是不会循环引用了,当是在ARC下会出现,因为self->self.blk->blockSelf->self,主要是__block修饰的self,实际是个block结构体,但是内部会持有self。

参考资料:
https://blog.csdn.net/u013378438/article/details/87167133
https://www.jianshu.com/p/ee9756f3d5f6
http://blog.devtang.com/2013/07/28/a-look-inside-blocks/
https://www.jianshu.com/p/fa76921131f9

1.前言

runloop跟iOS息息相关,当新建一个App的时候,就算没有新增任何代码,App也能一直运行下去,是因为UIApplicationMain就已经启动了一个runloop。

runloop代码本质上是一个do.while循环。一般线程执行完代码后就会退出,所以线程配合runloop就能实现线程常驻,让线程处于:接受消息-等待-处理的循环中。大部分框架都有类似的机制,比如Node.js的Event loop。

2.创建

runloop在iOS中分别有CoreFoundation层的CFRunloop,是线程安全的,还有Foundation层的NSRunloop,基于CFRunloop的封装,非线程安全。

runloop不能手动创建,只有在获取的时候,系统才会去创建,获取的方法有两个:

CFRunLoopGetMain()  //获取主线程
CFRunLoopGetCurrent()   //获取当前线程

runloop和线程是一一对应的,有个全局的Dictionary保存着两者,其中key是pthread_t,Value是CFRunloopRef,runloop的创建和pthread_t对应大概是下面的形式:

/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 访问 loopsDic 时的锁
static CFSpinLock_t loopsLock;

/// 获取一个 pthread 对应的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
    OSSpinLockLock(&loopsLock);
    if (!loopsDic) {
        // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。
        loopsDic = CFDictionaryCreateMutable();
        CFRunLoopRef mainLoop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
    }
    /// 直接从 Dictionary 里获取。
    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
    if (!loop) {
        /// 取不到时,创建一个
        loop = _CFRunLoopCreate();
        CFDictionarySetValue(loopsDic, thread, loop);
        /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。
        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
    }
    OSSpinLockUnLock(&loopsLock);
    return loop;
}

CFRunLoopRef CFRunLoopGetMain() {
    return _CFRunLoopGet(pthread_main_thread_np());
}

CFRunLoopRef CFRunLoopGetCurrent() {
    return _CFRunLoopGet(pthread_self());
}

3.数据结构

runloop主要有5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer,大概关系如下所示:

每次调用runloop时,只能指定一个Mode,也就是CurrentMode,要切换Mode需要退出runloop重新指定,主要是让Mode里的事件相互不影响。

CFRunLoopSourceRef
source0只有一个函数指针回调,不能主动触发,使用时需先调用CFRunLoopSourceSignal(source)标记为待处理,然后调用CFRunLoopWakeUp(runloop)唤醒runloop处理事件。
Source1

CFRunLoopTimerRef
包含一个时间长度和指针回调,加入runloop时,会注册相应时间点,时间点到后,runloop会唤醒回调。

CFRunLoopObserverRef
包含一个指针回调,主要观察runloop的状态,但状态发生变更时,runloop会触发相关的回调。
runloop有下面几种状态:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};

4.Mode

runloop的数据结构大概如下:

struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};

这里有个commonModes,通过CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);将Mode添加到commonMode里可以使Mode带有common属性,runloop内容发生改变时,会自动将commonModeItem里的事件同步到commonMode里的Mode里。
因为Mode会切换,比如滑动时会切换到UITrackingRunLoopMode,这时你注册的事件是不会在runloop触发的,只有将事件添加到commonModeItem里,才会同步到所有commonMode里。

Mode只能通过modeName来访问,比如CFRunLoopRunInMode(CFStringRef modeName, ...);

添加事件的方法有下面几个:

CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);

5.内部流程

runloop的流程图大概如下:

其中最重要的底层的是休眠那步,这里调用的是Mach的mach_msg()函数,Mach是底层内核。
在iOS硬件层上面的三个组成部分:Mach、BSD、IOKit (还包括一些上面没标注的内容),共同组成了 XNU 内核。
XNU 内核的内环被称作 Mach,其作为一个微内核,仅提供了诸如处理器调度、IPC (进程间通信)等非常少量的基础服务。
BSD 层可以看作围绕 Mach 层的一个外环,其提供了诸如进程管理、文件系统和网络等功能。
IOKit 层是为设备驱动提供了一个面向对象(C++)的一个框架

当线程处于等待状态时,点击暂停,会看到正处于mach_msg_trap()的函数中。

OBserves:
iOS本身会监听runloop的状态并回调出去,下面列举下常见的回调,以后通过观察堆栈就能具体定位到处于runloop的哪个阶段:

{
    /// 1. 通知Observers,即将进入RunLoop
    /// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush();
  __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
    do {
        /// 2. 通知 Observers: 即将触发 Timer 回调。 
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
        /// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);

        /// 4. 触发 Source0 (非基于port的) 回调。
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);

        /// 6. 通知Observers,即将进入休眠
        /// 此处有Observer释放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);

        /// 7. sleep to wait msg.
        mach_msg() -> mach_msg_trap();

        /// 8. 通知Observers,线程被唤醒
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);

        /// 9. 如果是被Timer唤醒的,回调Timer
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);

        /// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block
        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);

        /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);

    } while (...);

    /// 10. 通知Observers,即将退出RunLoop
    /// 此处有Observer释放AutoreleasePool: _objc_autoreleasePoolPop();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}

6.应用

AutoreleasePool
APP启动后注册了两个通知,回调到_wrapRunLoopWithAutoreleasePoolHandler()
第一个通知是Entry时,回调会调用_objc_autoreleasePoolPush()创建自动释放池。
第二个通知是BeforeWaiting时,回调会调用_objc_autoreleasePoolPop()_objc_autoreleasePoolPush()释放旧的池并创建新池。
在Exit时,也会调用_objc_autoreleasePoolPop(),完成循环。

事件响应
iOS注册了一个source1的事件,回调到__IOHIDEventSystemClientQueueCallback(),然后会继续调用_UIApplicationHandleEventQueue(),把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发。

手势识别
当上面的 _UIApplicationHandleEventQueue()识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。
苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是_UIGestureRecognizerUpdateObserver(),其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。
当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时,这个回调都会进行相应处理。

界面更新
当UIView/CAlayer的Frame被修改或者被设置为setNeedLayout时,会被标记为待处理,然后保存到全局容器中,苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:
_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。

PerformSelecter
PerformSelecter是通过生成一个timer到runloop实现的。

GCD
当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()里执行这个 block.

参考资料:

深入理解RunLoop


https://github.com/CoderLN/Runtime-RunLoop

1.object的内存分布

首先我们看下Object的结构体:

struct objc_class : objc_object {
    isa_t isa;
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
};

下面分别讲下这几个字段的作用。

1.isa_t *isa

在OC2.0中,对象的方法并没有存储在对象的结构体中,因为太占用内存,当实例方法被调用时,先通过isa来查找对应的Class,然后再Class里的class_data_bits_t指向的数据区域中查找对应的方法实现。为了使类方法也能走同样的逻辑,于是引入了MetaClass,让Classisa指向MetaClass

调用实例方法时,通过对象的isa指向的Class里的class_data_bits_t,查找对应的方法。
调用类方法时,通过Class里的isa指向的MetaClass的class_data_bits_t,查找对应的类方法。

下面我们具体看下isa的结构:

#define ISA_MASK        0x00007ffffffffff8ULL
#define ISA_MAGIC_MASK  0x001f800000000001ULL
#define ISA_MAGIC_VALUE 0x001d800000000001ULL
#define RC_ONE   (1ULL<<56)
#define RC_HALF  (1ULL<<7)

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

    struct {
        uintptr_t indexed           : 1;//objc最新源码命名改为nonpointer
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44;
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
    };
};

isa是一个union类型的isa_t结构体。也就是里面的cls,bits共用一块内存,占用内存以最大的为准,也就是64位,对里面的成员赋值会覆盖其他成员的值。

isa的初始化:

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    assert(!cls->instancesRequireRawIsa());
    assert(hasCxxDtor == cls->hasCxxDtor());
    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    if (!nonpointer) {
        isa.cls = cls;
    } else {
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());
        isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
        isa = newisa;
    }
}

注:本文提到的indexed在objc最新源码已改名为nonpointer。
可以看到nonpointer(indexed)是固定传true,因为现在iOS已经迁移到64位系统了,也就是目前逻辑基本直走else分支,然后是SUPPORT_INDEXED_ISA

#if __ARM_ARCH_7K__ >= 2  ||  (__arm64__ && !__LP64__)
#   define SUPPORT_INDEXED_ISA 1
#else
#   define SUPPORT_INDEXED_ISA 0
#endif

SUPPORT_INDEXED_ISA = 1是另外一种优化,用isa内indexcls存储着类在类列表内的索引,用于apple watch设备。
然后就是newisa.bits = ISA_MAGIC_VALUE,这里给bits赋初始,ISA_MAGIC_VALUE = 0x001d800000000001ULL,我们把它转化成2进制:

也就是ISA_MAGIC_VALUE只设置indexed以及magic这两个值。

然后设置isa.has_cxx_dtor = hasCxxDtor;代表是否有C++或Objc的析构器。

接着设置isa.shiftcls = (uintptr_t)cls >> 3;shiftcls用来存在对象实际的类指针,右移3位时为了8bit内存对齐。

#define ISA_MASK 0x00007ffffffffff8ULL
inline Class 
objc_object::ISA() 
{
    return (Class)(isa.bits & ISA_MASK);
}

上面是获取类指针的方法,也就是一个位运算返回isa.shiftcls
苹果之所以这么设计目的就是最大化利用64位的内存。

其他字段的作用:
has_assoc
对象含有或者曾经含有关联引用,没有关联引用的可以更快地释放内存
weakly_referenced
对象被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放
deallocating
对象正在释放内存
has_sidetable_rc
对象的引用计数太大了,存不下
extra_rc
对象的引用计数超过 1,会存在这个这个里面,如果引用计数为 10,extra_rc 的值就为 9。

2.cache_t *cache

cache用于缓存方法调用,当实例对象调用方法时,会优先在cache里查找,避免在method_list里遍历查找,效率低下,cache的数据结构如下:

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
    ... 省略其他方法
}

struct bucket_t {
private:
    cache_key_t _key;
    IMP _imp;
};

mask用来分配缓存的bucket总数
occupied表面实际占用的缓存butket个数
_buckets是一个散列表,而bucket_t里存储了指针与IMP的键值对。

3.class_data_bits_t *bit

class_data_bits_t的数据结构:

struct class_data_bits_t {
    uintptr_t bits;
}
// class_rw_t * plus custom rr/alloc flags

里面就一个64位的bits,注释说明class_data_bits_t相当于class_rw_t加上rr/alloc标识,class_rw_t用来保存类中的属性,方法还有协议。获取代码是:

class_rw_t* data() {
   return (class_rw_t *)(bits & FAST_DATA_MASK);
}

其中FAST_DATA_MASK = 0x00007ffffffffff8UL,也就是class_rw_t地址在class_data_bits_t的[3-46]位里,完整的class_data_bits_t属性分布如下图:


isSwift() 用于判断 Swift 类
hasDefaultRR()当前类或者父类含有默认的 retain/release/autorelease/retainCount/_tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference 方法
requiresRawIsa()当前类的实例需要 raw isa

下面看下class_rw_t结构:

struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;
};

flags标识了类的一些状态,涉及到声明周期和内存管理:

#define RW_REALIZED           (1<<31) // class_t->data is class_rw_t, not class_ro_t
#define RW_FUTURE             (1<<30) // class is unresolved future class
#define RW_INITIALIZED        (1<<29) // class is initialized
#define RW_INITIALIZING       (1<<28) // class is initializing
#define RW_COPIED_RO          (1<<27) // class_rw_t->ro is heap copy of class_ro_t
#define RW_CONSTRUCTING       (1<<26) // class allocated but not yet registered
#define RW_CONSTRUCTED        (1<<25) // class allocated and registered
// #define RW_24 (1<<24) // available for use; was RW_FINALIZE_ON_MAIN_THREAD
#define RW_LOADED             (1<<23) // class +load has been called
#if !SUPPORT_NONPOINTER_ISA
#define RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS (1<<22) // class instances may have associative references
#endif
#define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 21) // class has instance-specific GC layout
// #define RW_20       (1<<20) // available for use
#define RW_REALIZING          (1<<19) // class has started realizing but not yet completed it
#define RW_HAS_CXX_CTOR       (1<<18) // class or superclass has .cxx_construct implementation
#define RW_HAS_CXX_DTOR       (1<<17) // class or superclass has .cxx_destruct implementation
// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define RW_HAS_DEFAULT_AWZ    (1<<16)
#if SUPPORT_NONPOINTER_ISA
#define RW_REQUIRES_RAW_ISA   (1<<15) // class's instances requires raw isa

这里面的methods,可以再运行时添加,还有一个常量指针ro,是class_ro_t类型,其中存储了当前类在编译期就已经确定的属性,方法及遵守的协议。method_array_t, property_array_t, protocol_array_t 都继承自list_array_tt<Element, List>,可以不断扩张,class_rw_t的内容是可以在运行时被动态修改的。

class_ro_t的结构如下:

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
    uint32_t reserved;

    const uint8_t * ivarLayout;

    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

这里flags存储了类的信息,有一下几种定义:

#define RO_META               (1<<0) // class is a metaclass
#define RO_ROOT               (1<<1) // class is a root class
#define RO_HAS_CXX_STRUCTORS  (1<<2) // class has .cxx_construct/destruct implementations
// #define RO_HAS_LOAD_METHOD    (1<<3) // class has +load implementation
#define RO_HIDDEN             (1<<4) // class has visibility=hidden set
#define RO_EXCEPTION          (1<<5) // class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak
// #define RO_REUSE_ME           (1<<6) // this bit is available for reassignment
#define RO_IS_ARC             (1<<7) // class compiled with ARC
#define RO_HAS_CXX_DTOR_ONLY  (1<<8) // class has .cxx_destruct but no .cxx_construct (with RO_HAS_CXX_STRUCTORS)
#define RO_HAS_WEAK_WITHOUT_ARC (1<<9) // class is not ARC but has ARC-style weak ivar layout 

#define RO_FROM_BUNDLE        (1<<29) // class is in an unloadable bundle - must never be set by compiler
#define RO_FUTURE             (1<<30) // class is unrealized future class - must never be set by compiler
#define RO_REALIZED           (1<<31) // class is realized - must never be set by compiler

instanceStartinstanceSize用于确定类编译好时所占用的内存区域起始偏移和大小,用于non-fragile 特性。

method_list_t, ivar_list_t, property_list_t 结构体都继承自 entsize_list_tt<Element, List, FlagMask>

realizeClass
在编译期间类的结构中的 class_data_bits_t *data指向的是一个 class_ro_t * 指针:realizeClass在类第一次初始化后被调用,它会开辟class_rw_t的空间,并将 class_ro_t指针赋值给 class_rw_t->ro,大概源码如下:

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }

但是,在这段代码运行之后 class_rw_t 中的方法,属性以及协议列表均为空。这时需要 realizeClass 调用 methodizeClass 方法来将类自己实现的方法(包括分类)、属性和遵循的协议加载到 methodspropertiesprotocols 列表中。

methodizeClass里实现代码如下:

    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        rw->methods.attachLists(&list, 1);
    }

    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rw->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rw->protocols.attachLists(&protolist, 1);
    }

4.Category

Category可以给类提供扩展,它是一个category_t结构体指针:

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

里面存储了每个类的category的实例方法,类方法,协议,还有成员属性对应的指针,在APP启动加载镜像后,会向class_rw_t里添加对应的指针,大概函数调用流程是:

_objc_init_image->_read_images->_getObjc2CategoryList->addUnattachedCategoryForClass->remethodizeClass->attachCategories

2.SideTable(引用计数表,weak表)

SideTables表是一个全局的Hash表,用来管理所有对象的引用计数和weak指针,在多线程操作问题,于是SideTables会非常非常频繁的被操作,估不能对整个表加锁,苹果使用的是分离锁的技术。
SideTables实质是个C++的StripedMap模板,key是objc的内存地址,然后value是SideTable,因为SideTables在iOS上长度只有64,所以会有多个objc共用一个SideTable,里存储着对象的引用计数表和weak表。

struct SideTable {
    spinlock_t slock;   //自旋锁
    RefcountMap refcnts;    //引用计数表
    weak_table_t weak_table;    //weak表
}

1.引用计数表

refcnts也是一个Hash map,key是对象的具体地址,而value就是引用计数,具体代码是:

uintptr_t
objc_object::sidetable_retainCount()
{
    SideTable *table = SideTable::tableForPointer(this);

    size_t refcnt_result = 1;

    spinlock_lock(&table->slock);
    RefcountMap::iterator it = table->refcnts.find(this);
    if (it != table->refcnts.end()) {
        // this is valid for SIDE_TABLE_RC_PINNED too
        refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
    }
    spinlock_unlock(&table->slock);
    return refcnt_result;
}

源码里>> SIDE_TABLE_RC_SHIFTSIDE_TABLE_RC_SHIFT=2,也就是先右移两位,因为第一个Bit表示是否有weak对象,第二个表示是否正在dealoc,第三个开始才是存储引用计数数值的地方。
结果+1表示存储的是-1的结果,估值这么设计目的是节省内存,避免每次新创建对象都要存引用指数。

2.weak表

weak表的结构如下:

struct weak_table_t {
    weak_entry_t *weak_entries;        // hash数组,用来存储弱引用对象的相关信息weak_entry_t
    size_t    num_entries;             // hash数组中的元素个数
    uintptr_t mask;                    // hash数组长度-1,会参与hash计算。(注意,这里是hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个)
    uintptr_t max_hash_displacement;   // 可能会发生的hash冲突的最大次数
};

weak_table_t 就是一个动态增长的哈希表,当 weak_table 里的弱引用条目达到它容量的四分之三时,便会将容量拓展为两倍。具体代码如下:

#define TABLE_SIZE(entry) (entry->mask ? entry->mask + 1 : 0)
// Grow the given zone's table of weak references if it is full.
static void weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);
    // Grow if at least 3/4 full.
    if (weak_table->num_entries >= old_size * 3 / 4) {
        weak_resize(weak_table, old_size ? old_size*2 : 64);
    }
}

当然除了扩大,还有缩小,当表本身大于等于 1024 并且存放了不足十六分之一的条目时,直接缩小 8 倍。代码如下:

static void weak_compact_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(weak_table);
    // Shrink if larger than 1024 buckets and at most 1/16 full.
    if (old_size >= 1024  && old_size / 16 >= weak_table->num_entries) {
        weak_resize(weak_table, old_size / 8);
        // leaves new table no more than 1/2 full
    }
}

两个实际都是调用了weak_resize,代码实现如下:

static void weak_resize(weak_table_t *weak_table, size_t new_size)
{
    size_t old_size = TABLE_SIZE(weak_table);

    weak_entry_t *old_entries = weak_table->weak_entries;
    weak_entry_t *new_entries = (weak_entry_t *)
        calloc(new_size, sizeof(weak_entry_t));

    weak_table->mask = new_size - 1;
    weak_table->weak_entries = new_entries;
    weak_table->max_hash_displacement = 0;
    weak_table->num_entries = 0;  // restored by weak_entry_insert below

    if (old_entries) {
        weak_entry_t *entry;
        weak_entry_t *end = old_entries + old_size;
        for (entry = old_entries; entry < end; entry++) {
            if (entry->referent) {
                weak_entry_insert(weak_table, entry);
            }
        }
        free(old_entries);
    }
}

实际过程就是新建一个new_size大小的weak_entry_t *new_entries赋值给weak_table,然后将old_entries遍历重新插入进去。

插入由weak_entry_insert实现,具体代码为:

static void weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    size_t begin = hash_pointer(new_entry->referent) & (weak_table->mask);
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_entries);
        hash_displacement++;
    }

    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

hash_pointer(new_entry->referent) & (weak_table->mask)的作用相当于取余,应为表的大小是2的倍数,大概流程就是while循环判断index为nil的地方,然后赋值new_entry,同时更新num_entriesmax_hash_displacement

删除的流程就简单点了,在weak_entry_remove里:

static void weak_entry_remove(weak_table_t *weak_table, weak_entry_t *entry)
{
    // 直接free释放
    if (entry->out_of_line()) free(entry->referrers);
    bzero(entry, sizeof(*entry));
    weak_table->num_entries--;//减1
    weak_compact_maybe(weak_table);//判断是否缩小weak_table
}

接下来我们看下weak_entry_t是怎么定义的:

#define WEAK_INLINE_COUNT 4
#define REFERRERS_OUT_OF_LINE 2

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;//等同于objc_object *referent
    union {
        struct {
            weak_referrer_t *referrers;//用来存放弱引用变量的地址
            uintptr_t        out_of_line_ness : 2;//用来标识是哪种结构体
            uintptr_t        num_refs : PTR_MINUS_2;//用来存储 referrers 的大小
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            //inline 存储,当大于 4 时,再变成第一种的动态哈希表存储
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };
};

最后我们看下对象dealoc是,weak对象是怎么自动变为nil的,具体实现在weak_clear_no_lock里,相关代码如下:

void 
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
{
    //找到weak_entry_t
    objc_object *referent = (objc_object *)referent_id;
    weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
    if (entry == nil) {
        /// XXX shouldn't happen, but does with mismatched CF/objc
        //printf("XXX no entry for clear deallocating %p\n", referent);
        return;
    }

    // zero out references
    weak_referrer_t *referrers;
    size_t count;
    //获取weak_entry_t里的referrers
    if (entry->out_of_line()) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry);
    } 
    else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    //遍历将referrers里的对象置nil
    for (size_t i = 0; i < count; ++i) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
            else if (*referrer) {
                _objc_inform("__weak variable at %p holds %p instead of %p. "
                             "This is probably incorrect use of "
                             "objc_storeWeak() and objc_loadWeak(). "
                             "Break on objc_weak_error to debug.\n", 
                             referrer, (void*)*referrer, (void*)referent);
                objc_weak_error();
            }
        }
    }
    //移除entry
    weak_entry_remove(weak_table, entry);
}

在访问一个弱引用时,ARC 会对其进行一些操作:

obj = weakObj;

// 会变成
objc_loadWeakRetained(&weakObj);
obj = weakObj;
objc_release(weakObj);

objc_loadWeakRetained 函数的主要作用就是调用了 rootTryRetain 函数。

3. __strong/__weak

1.strong

在strong和weak是ARC新增的,在ARC中,创建对象时默认都是__strong修饰,而给__strong变量赋值时,实际会调用objc_storeStrong

obj = otherObj;
// 会变成如下函数调用
objc_storeStrong(&obj, otherObj);

objc_storeStrong 的实现如下:

void
objc_storeStrong(id *location, id obj)
{
    id prev = *location;
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;
    objc_release(prev);
}

也即是,objc_storeStrong会对右边的对象进行objc_retain操作,而原来的对象会objc_release掉 ,当变量离开作用域时,实际会调用objc_storeStrong(&obj, nil);也就是objc_release(obj),等同于obj = nil

如果对象非alloc/new/copy/mutableCopy返回的,__strong又是如何工作的呢,在MRC下:

+ (id) array {
    return [[NSMutableArray alloc] init];
}

上面代码有个问题在ARC很常见,也没有问题,当是在MRC就有问题了,因为 alloc/init之后是个局部变量,出了作用域后就释放了,又如何return出来呢,所以在MRC下需要在后面添加autorelease

//MRC
+ (id) array {
    return [[[NSMutableArray alloc] init] autorelease];
}

加了autorelease后,达到了既能返回又能自动释放的功能。那么ARC又是如何完成这个功能呢,实际上在ARC,编译器会帮我们插入代码变成这样:

+ (id) array {
    NSMutableArray *array =  [[NSMutableArray alloc] init];
    objc_autoreleaseReturnValue(array);
}

ARC下还做了一些优化,objc_autoreleaseReturnValue大概逻辑就是判断对象外部是否被持有,能则直接返回对象,不能则将对象放到autoreleasepool里再返回。
在外部的逻辑中:

[NSArray array];
//没有外部持有时会转换成
objc_unsafeClaimAutoreleasedReturnValue([NSArray array]);

NSArray *array = [NSArray array];
//有外部持有时
id array = objc_retainAutoreleasedReturnValue([NSArray array]);

没外部持有时,创建出来的对象应该被销毁,所以调用objc_unsafeClaimAutoreleasedReturnValue销毁对象,并把前面的autorelease优化标记清空。
在有外部持有时,创建出来的对象不需要销毁,所以objc_retainAutoreleasedReturnValue查询前面的autorelease优化标记,有则直接返回,不在retain。

总的来说,__strong对象编译器会自动帮我们判断对象是否直接返回,还是autorelease,还是retain。

2.weak

weak的作用是引用一个对象但是不进行retain,当对象销毁时,会在全局weak表里查找相关的引用,并移除。先看下weak对象是怎么创建的:

{
    id __weak weakObj = strongObj;
}
// 会变成
{
    id __weak weakObj;
    objc_initWeak(&weakObj, strongObj);
    // 离开变量的范围,进行销毁
    objc_destroyWeak(&weakObj);
}

这里objc_initWeak里直接调用了storeWeak函数,具体代码实现比较长,如下:

// Template parameters.
enum HaveOld { DontHaveOld = false, DoHaveOld = true };
enum HaveNew { DontHaveNew = false, DoHaveNew = true };
enum CrashIfDeallocating {
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};

template <HaveOld haveOld, HaveNew haveNew,
          CrashIfDeallocating crashIfDeallocating>
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

 retry:
    if (haveOld) { // 如果weak ptr之前弱引用过一个obj,则将这个obj所对应的SideTable取出,赋值给oldTable
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil; // 如果weak ptr之前没有弱引用过一个obj,则oldTable = nil
    }
    if (haveNew) { // 如果weak ptr要weak引用一个新的obj,则将该obj对应的SideTable取出,赋值给newTable
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil; // 如果weak ptr不需要引用一个新obj,则newTable = nil
    }

    // 加锁操作,防止多线程中竞争冲突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    // location 应该与 oldObj 保持一致,如果不同,说明当前的 location 已经处理过 oldObj 可是又被其他线程所修改
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized())  // 如果cls还没有初始化,先初始化,再尝试设置weak
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

            previouslyInitializedClass = cls; // 这里记录一下previouslyInitializedClass, 防止改if分支再次进入
            goto retry; // 重新获取一遍newObj,这时的newObj应该已经初始化过了
        }
    }

    // Clean up old value, if any.
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location); // 如果weak_ptr之前弱引用过别的对象oldObj,则调用weak_unregister_no_lock,在oldObj的weak_entry_t中移除该weak_ptr地址
    }

    // Assign new value, if any.
    if (haveNew) { // 如果weak_ptr需要弱引用新的对象newObj
        // (1) 调用weak_register_no_lock方法,将weak ptr的地址记录到newObj对应的weak_entry_t中
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // (2) 更新newObj的isa的weakly_referenced bit标志位
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        // (3)*location 赋值,也就是将weak ptr直接指向了newObj。可以看到,这里并没有将newObj的引用计数+1
        *location = (id)newObj; // 将weak ptr指向object
    }
    else {
        // No new value. The storage is not changed.
    }
    // 解锁,其他线程可以访问oldTable, newTable了
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);

    return (id)newObj; // 返回newObj,此时的newObj与刚传入时相比,weakly-referenced bit位置1
}

大概流程就是先对参数haveOld``haveNew做判断,如果有haveOld,则调用weak_unregister_no_lock在oldObj的weak_entry_t中移除weak_ptr,如果有haveNew,则调用weak_register_no_lockweak ptr的地址记录到newObj对应的weak_entry_t中,然后更新isa里的weakly_referenced标志位,表示被weak引用了。

我们先看注册函数:weak_register_no_lock

id 
weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    // 如果referent为nil 或 referent 采用了TaggedPointer计数方式,直接返回,不做任何操作
    if (!referent  ||  referent->isTaggedPointer()) return referent_id;

    // 确保被引用的对象可用(没有在析构,同时应该支持weak引用)
    bool deallocating;
    if (!referent->ISA()->hasCustomRR()) {
        deallocating = referent->rootIsDeallocating();
    }
    else {
        BOOL (*allowsWeakReference)(objc_object *, SEL) = 
            (BOOL(*)(objc_object *, SEL))
            object_getMethodImplementation((id)referent, 
                                           SEL_allowsWeakReference);
        if ((IMP)allowsWeakReference == _objc_msgForward) {
            return nil;
        }
        deallocating =
            ! (*allowsWeakReference)(referent, SEL_allowsWeakReference);
    }
    // 正在析构的对象,不能够被弱引用
    if (deallocating) {
        if (crashIfDeallocating) {
            _objc_fatal("Cannot form weak reference to instance (%p) of "
                        "class %s. It is possible that this object was "
                        "over-released, or is in the process of deallocation.",
                        (void*)referent, object_getClassName((id)referent));
        } else {
            return nil;
        }
    }

    // 在 weak_table中找到referent对应的weak_entry,并将referrer加入到weak_entry中
    weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) { 
        // 如果能找到weak_entry,则讲referrer插入到weak_entry中
        append_referrer(entry, referrer);   // 将referrer插入到weak_entry_t的引用数组中
    } 
    else { // 如果找不到,就新建一个
        weak_entry_t new_entry(referent, referrer);  
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }
    return referent_id;
}

虽然实现代码有点长,但是前面基本都是deallocating的代码判断,正在dealoc的对象是不能被weak引用的,接下来才是weak_table的操作,通过entry = weak_entry_for_referent(weak_table, referent)判断是否已经存在weak_entry,有则append_referrer(entry, referrer);添加,没有则通过new_entry(referent, referrer)新增一个weak_entry_t,然后weak_grow_maybe(weak_table)判断是否扩容,在weak_entry_insert(weak_table, &new_entry);插入到weak_table里。

weak_unregister_no_lock的实现就相对简单多了:

void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id, 
                        id *referrer_id)
{
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

    weak_entry_t *entry;
    if (!referent) return;
    if ((entry = weak_entry_for_referent(weak_table, referent))) { // 查找到referent所对应的weak_entry_t
        remove_referrer(entry, referrer);  // 在referent所对应的weak_entry_t的hash数组中,移除referrer

        // 移除元素之后, 要检查一下weak_entry_t的hash数组是否已经空了
        bool empty = true;
        if (entry->out_of_line()  &&  entry->num_refs != 0) {
            empty = false;
        }
        else {
            for (size_t i = 0; i < WEAK_INLINE_COUNT; i++) {
                if (entry->inline_referrers[i]) {
                    empty = false; 
                    break;
                }
            }
        }
        if (empty) { // 如果weak_entry_t的hash数组已经空了,则需要将weak_entry_t从weak_table中移除
            weak_entry_remove(weak_table, entry);
        }
    }
}

就是通过weak_entry_for_referent(weak_table, referent)查找weak_entry_t里的referent,然后remove_referrer(entry, referrer);移除掉,最后再判断weak_entry_t是否为空,为空则weak_entry_remove(weak_table, entry);移除entry。

weak的基本流程大概就是这样,还有个函数是objc_storeWeak这个是当我们的weakObj已经指向一个weak对象,又要指向新的weak对象时才会调用的。

4.Alloc/init

创建一个对象时,需要先AllocAlloc就是分配内存,实际最后会调用C函数calloc()来分配内存。大概调用流程如下:

id
objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}

id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

首先alloc会调用_objc_rootAlloc,然后又会调用callAlloc,也就是具有实现在callAlloc里,!cls->ISA()->hasCustomAWZ()这里表示该类没有自定义+allocWithZone:的实现,有的话走下面的[cls allocWithZone:nil]

接下来我们看cls->canAllocFast(),查看具体实现是:

#if FAST_ALLOC
    bool canAllocFast() {
        return bits & FAST_ALLOC;
    }
#else
    bool canAllocFast() {
        return false;
    }
#endif

也就是由FAST_ALLOC宏定义来控制,查看FAST_ALLOC的定义发现目前都是0,也就是到现在为止,FAST_ALLOC功能都没开启,也就是会走下面的else分支代码了。

接下来看id obj = class_createInstance(cls, 0);这里class_createInstance里是直接调用_class_createInstanceFromZone,我们看下他的代码实现:

static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;
    //断言 cls 是已经 realize
    assert(cls->isRealized());

    // 这个类是否有 C++ 类构造函数和析构函数
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    //这个类是否使用isa_t 类型的 isa
    bool fast = cls->canAllocNonpointer();
    //获取实例的内存大小,从 cls 的 ro 中获得 instanceSize 然后将它对齐,并加上 extraBytes,最小16字节,extraBytes参数是0
    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;
        obj->initIsa(cls);
    }
    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }
    return obj;
}

可以看到最后是calloc(1, size)申请相应大小的内存后,初始化isainitInstanceIsa,关于isa的初始化上面已经有说明,至此alloc的流程就走完了,其实alloc基本就完成了对象的创建和初始化。

2.init

init的runtime实现非常简单:

- (id)init {
    return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
    return obj;
}

实际上就是return self,估计init主要就是开放给外部重新实现的。

5.Retain/Release

retain和release虽然在ARC时代不需要手动调用了,但实际上只是编译器帮我们自动生成了代码,retain和release的功能仍然是存在的。

1.Retain

retain的具体实现是objc_object::retain,代码是:

inline id 
objc_object::retain()
{
    assert(!isTaggedPointer());
    if (fastpath(!ISA()->hasCustomRR())) {
        return rootRetain();
    }
    return ((id(*)(objc_object *, SEL))objc_msgSend)(this, SEL_retain);
}

这里!ISA()->hasCustomRR()判断是否有自己实现了retain,有的话直接用objc_msgSend调用方法,没有的话走系统实现的rootRetain,具体代码如下:

ALWAYS_INLINE id 
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
    if (isTaggedPointer()) return (id)this;

    //示 side table 是否锁上了
    bool sideTableLocked = false;
    //表示是否需要将 isa 中的引用计数转移到 side table 
    bool transcribeToSideTable = false;

    isa_t oldisa;//isa 本来的值
    isa_t newisa;//isa 新的值(增加了引用计数后的值)

    do {
        transcribeToSideTable = false;
        oldisa = LoadExclusive(&isa.bits);//isa赋值
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain();
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        if (slowpath(tryRetain && newisa.deallocating)) {
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked) sidetable_unlock();
            return nil;
        }
        uintptr_t carry;//溢出标识
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        if (slowpath(carry)) {//溢出了
            // newisa.extra_rc++ overflowed
            if (!handleOverflow) {
                ClearExclusive(&isa.bits);
                return rootRetain_overflow(tryRetain);
            }
            //只留下一半的retain count,另外一半放到sidetable里
            if (!tryRetain && !sideTableLocked) sidetable_lock();
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF;
            newisa.has_sidetable_rc = true;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));

    if (slowpath(transcribeToSideTable)) {
        // 添加剩下的一半retain counts到side table.
        sidetable_addExtraRC_nolock(RC_HALF);
    }

    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    return (id)this;
}

大概逻辑就是先让isa里的extra_rc自增1,如果没溢出,则返回,如果有溢出,则取出一半,放到前面说到的side table里。在64位iOS设备上,extra_rc能存19位,也就是524288,可以说基本不会溢出,在x86_64上,只有8位,也就是只能存255。

2.release

release前面流程和retain一样的,不过一个是自增,一个是自减,release多了个流程就是自减为0后,需要走delloc流程,具体代码如下:

ALWAYS_INLINE bool 
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
    if (isTaggedPointer()) return false;
    bool sideTableLocked = false;

    isa_t oldisa;
    isa_t newisa;

 retry:
    do {
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) sidetable_unlock();
            return sidetable_release(performDealloc);
        }
        // don't check newisa.fast_rr; we already called any RR overrides
        uintptr_t carry;
        newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
        if (slowpath(carry)) {
            // don't ClearExclusive()
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                             oldisa.bits, newisa.bits)));

    if (slowpath(sideTableLocked)) sidetable_unlock();
    return false;

 underflow:
    // newisa.extra_rc-- underflowed: borrow from side table or deallocate
    // abandon newisa to undo the decrement
    newisa = oldisa;

    if (slowpath(newisa.has_sidetable_rc)) {
        //标记为处理溢出,然后重新调用该方法
        if (!handleUnderflow) {
            ClearExclusive(&isa.bits);
            return rootRelease_underflow(performDealloc);
        }

        //先加锁,然后重新跑 retry流程
        if (!sideTableLocked) {
            ClearExclusive(&isa.bits);
            sidetable_lock();
            sideTableLocked = true;
            // Need to start over to avoid a race against 
            // the nonpointer -> raw pointer transition.
            goto retry;
        }

        //下面的代码就是从side table里取出放到isa的extra_rc里
        // Try to remove some retain counts from the side table.
        size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);

        // To avoid races, has_sidetable_rc must remain set 
        // even if the side table count is now zero.

        if (borrowed > 0) {
            // Side table retain count decreased.
            // Try to add them to the inline count.
            newisa.extra_rc = borrowed - 1;  // redo the original decrement too
            bool stored = StoreReleaseExclusive(&isa.bits, 
                                                oldisa.bits, newisa.bits);
            if (!stored) {
                // Inline update failed. 
                // Try it again right now. This prevents livelock on LL/SC 
                // architectures where the side table access itself may have 
                // dropped the reservation.
                isa_t oldisa2 = LoadExclusive(&isa.bits);
                isa_t newisa2 = oldisa2;
                if (newisa2.nonpointer) {
                    uintptr_t overflow;
                    newisa2.bits = 
                        addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                    if (!overflow) {
                        stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                       newisa2.bits);
                    }
                }
            }

            if (!stored) {
                // Inline update failed.
                // Put the retains back in the side table.
                sidetable_addExtraRC_nolock(borrowed);
                goto retry;
            }

            // Decrement successful after borrowing from side table.
            // This decrement cannot be the deallocating decrement - the side 
            // table lock and has_sidetable_rc bit ensure that if everyone 
            // else tried to -release while we worked, the last one would block.
            sidetable_unlock();
            return false;
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }
    //dealloc流程
    // Really deallocate.
    if (slowpath(newisa.deallocating)) {
        ClearExclusive(&isa.bits);
        if (sideTableLocked) sidetable_unlock();
        return overrelease_error();
        // does not actually return
    }
    newisa.deallocating = true;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;

    if (slowpath(sideTableLocked)) sidetable_unlock();

    __sync_synchronize();
    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
    }
    return true;
}

大概流程就是,先自减1,减到溢出就到side table里取,直到最后都为0了,则使用objc_msgSend调用dealloc。

6.Dealloc

从上文可知当release后引用计数为0时,会调用dealloc方法,然后会调用objc_object::rootDealloc,具体代码如下:

inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?
    //判断是否有weak引用,是否有关联对象,是否有c++析构函数,是否有sidetable引用计数
    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

做了一堆判断后,如果符合释放条件,则直接调用free,和alloc时调用calloc对应,不符合的话,则走object_dispose

id 
object_dispose(id obj)
{
    if (!obj) return nil;
    objc_destructInstance(obj);
    free(obj);
    return nil;
}

void *objc_destructInstance(id obj)
{
    if (obj) {
        // Read all of the flags at once for performance.
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();
        // This order is important.
        if (cxx) object_cxxDestruct(obj);// 调用析构函数
        if (assoc) _object_remove_assocations(obj);// 移除所有的关联对象
        obj->clearDeallocating();
    }
    return obj;
}

inline void 
objc_object::clearDeallocating()
{
    if (slowpath(!isa.nonpointer)) {
        // Slow path for raw pointer isa.
        sidetable_clearDeallocating();
    }
    else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
        // Slow path for non-pointer isa with weak refs and/or side table data.
        clearDeallocating_slow();
    }
    assert(!sidetable_present());
}

NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
    assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));

    SideTable& table = SideTables()[this];
    table.lock();
    if (isa.weakly_referenced) {
        //将weak 引用置为nil,并在weak_table_t中删除对象节点
        weak_clear_no_lock(&table.weak_table, (id)this);
    }
    if (isa.has_sidetable_rc) {
        //如果有side table计数借位,则side table中对应的节点移除
        table.refcnts.erase(this);
    }
    table.unlock();
}

1.object_cxxDestruct

这里object_cxxDestruct底层直接调用object_cxxDestructFromClass方法:

static void object_cxxDestructFromClass(id obj, Class cls)
{
    void (*dtor)(id);
    // Call cls's dtor first, then superclasses's dtors.
    for ( ; cls; cls = cls->superclass) {
        if (!cls->hasCxxDtor()) return; 
        dtor = (void(*)(id))
            lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
        if (dtor != (void(*)(id))_objc_msgForward_impcache) {
            if (PrintCxxCtors) {
                _objc_inform("CXX: calling C++ destructors for class %s", 
                             cls->nameForLogging());
            }
            (*dtor)(obj);
        }
    }
}

里面流程就是,沿着继承链逐层寻找SEL_cxx_destruct这个方法,然后调用。SEL_cxx_destruct实际上就是.cxx_destruct,这个原本是c++的析构函数,ARC中借用了这个方法,它遍历当前对象所有的实例变量(Ivars),调用objc_storeStrong,以达到自动释放成员变量的目的。

2._object_remove_assocations

这里主要移除该对象的关联对象,所有对象的关联对象存放在一个全局的AssociationsManager里,删除关联对象的代码如下:

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

AssociationsHashMap里,key是objc的地址,value是ObjectAssociationMap,然后遍历里面的所有ObjcAssociation对象,调用ReleaseValue(),最终调用到objc_release

3.weak_clear_no_lock

大概流程查看前面的__weak的实现。

4.table.refcnts.erase

7.Autoreleasepool

autoreleasepool顾名思议就是自动释放池,因为有些对象需要延迟释放,这时候需要放到池中等待自动释放。
我们正常用法是使用@autoreleasepool{},实际上等同于__AtAutoreleasePool __autoreleasepool,找到 __AtAutoreleasePool 的定义:

struct __AtAutoreleasePool {
    __AtAutoreleasePool() {
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
    ~__AtAutoreleasePool() {
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
    void * atautoreleasepoolobj;
};

这里有个构造函数和析构函数,代码利用了变量声明会调用构造函数,出作用域时调用析构函数的原理,实现了在代码块里插入objc_autoreleasePoolPush()objc_autoreleasePoolPop()的功能。类似:

objc_autoreleasePoolPush();
// Do your code
objc_autoreleasePoolPop(atautoreleasepoolobj);

这两个函数的实现是:

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

可以看到只是对AutoreleasePoolPage的简单封装,而AutoreleasePoolPage是一个C++类的双向链表。我们先看下AutoreleasePoolPage的成员变量:

class AutoreleasePoolPage
{
    magic_t const magic;//用来检查 AutoreleasePoolPage 的内存没有被修改的
    id *next;//存放的是下一个被 autorelease 的对象指针存放的地址
    pthread_t const thread;//对应的线程
    AutoreleasePoolPage * const parent;//autoreleasPool page双向链表的前指针
    AutoreleasePoolPage *child;//autoreleasPool page双向链表的后指针
    uint32_t const depth;//当前autoreleasPool page在双向链表中的深度
    uint32_t hiwat;//DEBUG 时才有用的参数,表示最高有记录过多少对象
}

AutoreleasePoolPage 重载了 new 操作符,申请了4096 字节的内存,除了存放成员变量外,其他地方用来存放需要autorelease的对象。内存结构图如下:

可以看出,begin 就是成员变量结束的地址,end 就是整个申请的内存结束的地方,next初始值是begin,随着对象的存放,会往end移动。
这里面有个POOL_BOUNDARY,相当于一个池边界,实际上就是nil。

1.push

当用户调用@autoreleasepool{}的时候,系统首先调用AutoreleasePoolPage::push()方法,我们来看下push的实现:

#define POOL_BOUNDARY nil
    static inline void *push() 
    {
        id *dest;
        if (DebugPoolAllocation) {
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;
    }

这里有个DebugPoolAllocation值我们先不看,主要看下面else分支,autoreleaseFast(POOL_BOUNDARY)接下来我们看下autoreleaseFast的实现:

    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            return page->add(obj);
        } else if (page) {
            return autoreleaseFullPage(obj, page);
        } else {
            return autoreleaseNoPage(obj);
        }
    }

可以看到流程就是先获取hotPage,然后判断是否full,否则进行add操作,是则autoreleaseFullPage,接下来是page为空时走autoreleaseNoPage流程。
hotPage就是当前线程对应的page,获取代码如下:

#   define EMPTY_POOL_PLACEHOLDER ((id*)1)
    static inline AutoreleasePoolPage *hotPage()
    {
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);//从线程tls中获取,key是固定值43
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();//检查完整性
        return result;
    }

add 方法的实现很简单,就是将 obj 存到 next 的位置,并将 next 加 1。

id *add(id obj)
{
    assert(!full());
    id *ret = next;  // faster than `return next-1` because of aliasing
    *next++ = obj;
     return ret;
}

autoreleaseFullPage也就是当前page满了,会查看child链表,找到没满的,找不到就new 一个新的AutoreleasePoolPage,然后进行add操作。

static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        assert(page == hotPage());
        assert(page->full()  ||  DebugPoolAllocation);

        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());

        setHotPage(page);
        return page->add(obj);
    }

autoreleaseNoPage的实现要复杂点:

static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {

        assert(!hotPage());
        //判断hotPage是否EMPTY_POOL_PLACEHOLDER
        bool pushExtraBoundary = false;
        if (haveEmptyPoolPlaceholder()) {
            pushExtraBoundary = true;
        }
        else if (obj == POOL_BOUNDARY) {
            //在tls中设置value为EMPTY_POOL_PLACEHOLDER
            return setEmptyPoolPlaceholder();
        }
        //新建一个AutoreleasePoolPage
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        setHotPage(page);
        //当前hotPage是EMPTY_POOL_PLACEHOLDER时add(POOL_BOUNDARY)
        if (pushExtraBoundary) {
            page->add(POOL_BOUNDARY);
        }
        return page->add(obj);
    }

push相关的操作基本就到此了,当第一次Push时,会先将tls里用EMPTY_POOL_PLACEHOLDER占位,表示是一个空池,然后第二次push的时候才会生成AutoreleasePoolPage,并add(POOL_BOUNDARY),然后才add(obj),主要是从内存角度考虑,毕竟创建一个page需要4096字节的内存。

2.pop

pop的实现和push 是相反的,其实autoreleasepool就是实现了入栈出栈,我们看具体代码实现:

    static inline void pop(void *token) 
    {
        AutoreleasePoolPage *page;
        id *stop;

        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            if (hotPage()) {
                // Pool was used. Pop its contents normally.
                // Pool pages remain allocated for re-use as usual.
                pop(coldPage()->begin());
            } else {
                // Pool was never used. Clear the placeholder.
                setHotPage(nil);
            }
            return;
        }

        page = pageForPointer(token);//获取token所在的page
        stop = (id *)token;
        //老sdk 的一些处理
        if (*stop != POOL_BOUNDARY) {
            if (stop == page->begin()  &&  !page->parent) {

            } else {
                //old SDKs才会走
                return badPop(token);
            }
        }
        //打印Hiwat
        if (PrintPoolHiwat) printHiwat();
        // 对page中的object做objc_release操作,一直到stop
        page->releaseUntil(stop);

       //Debug相关
        if (DebugPoolAllocation  &&  page->empty()) {
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            page->kill();
            setHotPage(nil);
        }
         // 删除多余的child,节约内存
        else if (page->child) {
            // 判断当前page是否满一半了,是则不删了
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

这里面对objc的释放其实是在releaseUntil函数里,剩下的就是对page的一些清空操作。我们看下releaseUntil的实现:

    void releaseUntil(id *stop) 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release 
            // autoreleased more objects
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            while (page->empty()) {
                page = page->parent;
                setHotPage(page);
            }
            id obj = *--page->next;
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));

            if (obj != POOL_BOUNDARY) {
                objc_release(obj);
            }
        }
        setHotPage(this);
    }

其实就是循环直到到 stop 给每个对象调用 objc_release 来释放对象。
另外,MRC时代的autorelease的调用栈是:objc_autorelease->rootAutorelease->rootAutorelease2->AutoreleasePoolPage::autorelease->autoreleaseFast。
也就是最后会走到上面提到的autoreleaseFast

3.应用

autoreleasepool一般是用在循环里,其实runloop每次循环都会进行push和pop操作,每一次消息循环开始的时候会先创建自动释放池,这次循环结束前,会释放自动释放池,然后RunLoop等待下次事件源。所以实际上,自动释放的对象,并不一定会在出了代码作用域后释放,而是在runloop循环结束后才释放。

当在一个循环里创建了很多自动释放的对象时,如果循环很久,就会导致需要好几次runloop才能将对象都释放完毕,造成内存飙升和泄漏。这时候可以再循环里手动添加autoreleasepool,让每次循环都能将对象释放完毕。

在OC中,非alloc/init/new/copy/mutableCopy之间创建出来的对象都会进入到autoreleasepool里,比如类方法初始化。

8.循环引用

带完善..

参考资料:
http://blog.sunnyxx.com/2014/10/15/behind-autorelease/
https://blog.csdn.net/u013378438/article/details/80733391

http://yulingtianxia.com/blog/2015/12/06/The-Principle-of-Refenrence-Counting/#isa-%E6%8C%87%E9%92%88%EF%BC%88NONPOINTER-ISA%EF%BC%89
https://www.jianshu.com/p/ef6d9bf8fe59

https://blog.csdn.net/u013378438/article/details/82790332
https://github.com/draveness/analyze/blob/master/contents/objc/%E4%BB%8E%20NSObject%20%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%86%E8%A7%A3%20isa.md#shiftcls
https://www.jianshu.com/p/d826c0c8ea50
https://www.jianshu.com/p/1956c09186ae

https://www.jianshu.com/p/77322786ee6e
https://blog.sunnyxx.com/2014/04/02/objc_dig_arc_dealloc/