block的类型
block有三种类型:堆block,栈block,全局block。
对于这三种类型的block遵循俩个原则:
block如果没有使⽤外部变量,或者只使⽤静态变量和全局变量,那⼀定是全局blcok。
block如果使⽤了外部变量,⽽且不是静态变量或全局变量,如果赋值给强引⽤的是堆block,
如果赋值给弱引⽤的是栈blcok。
为什么block要⽤copy关键字修饰
因为block在创建的时候,它的内存是分配在栈上的,⽽不是在堆上。栈区的特点是:对象随时有
可能被销毁,⼀旦被销毁,在调⽤的时候,就会造成系统的崩溃。所以我们要使⽤copy把它拷⻉
到堆上。在ARC下, 对于block使⽤copy与strong其实都⼀样, 因为block的retain就是⽤copy来实现
的, 所以在ARC下 block使⽤ copy 和 strong 都可以。
block的源码解析
#if TARGET_OS_WIN32 // win32 __sync_bool_compare_and_swap()函数
#define _CRT_SECURE_NO_WARNINGS 1
#include <windows.h>
static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
{
// fixme barrier is overkill -- see objc-os.h
long original = InterlockedCompareExchange(dst, newl, oldl);
return (original == oldl);
}
static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst)
{
// fixme barrier is overkill -- see objc-os.h
int original = InterlockedCompareExchange(dst, newi, oldi);
return (original == oldi);
}
#else
// __sync_bool_compare_and_swap 是 GCC 内建的原子操作函数, 执行CAS操作,比较 _Ptr 和 _Old。 如果相等就将 _New 放到 _Ptr 中,并且返回true,否则返回false
#define OSAtomicCompareAndSwapLong(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
#define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
#endif
/*******************************************************************************
Internal Utilities
********************************************************************************/
// 引用计数加 1,最多不超过 BLOCK_REFCOUNT_MASK
// volatile的作用是:作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化
static int32_t latching_incr_int(volatile int32_t *where) {
while (1) {
int32_t old_value = *where;
// 如果 old_value 在第 1~15 位都已经变为 1 了,即引用计数已经满了,就返回 BLOCK_REFCOUNT_MASK
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
return BLOCK_REFCOUNT_MASK;
}
// 比较 where 处的现在的值是否等于 old_value,如果等于,就将新值 oldValue + 2 放入 where
// 否则继续下一轮循环
// 这里加 2,是因为 flag 的第 0 位已经被占了,引用计数是第 1~15 位,所以加上 0b10,引用计数只是加 1
if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
return old_value+2;
}
}
}
// 当 block 不是处于 dealloc 时,引用计数加 1
// 返回值是是否成功,只有在 block 处于 dealloc 时,才会失败
static bool latching_incr_int_not_deallocating(volatile int32_t *where) {
while (1) {
int32_t old_value = *where;
// 如果 block 正在 dealloc,返回 false
if (old_value & BLOCK_DEALLOCATING) {
// if deallocating we can't do this
return false;
}
// 引用计数最多不超过 BLOCK_REFCOUNT_MASK
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
// if latched, we're leaking this block, and we succeed
return true;
}
// 引用计数加 1,这里 old_value+2 的原因和 latching_incr_int 一致
// 如果失败,进行下一轮循环
if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
// otherwise, we must store a new retained value without the deallocating bit set
return true;
}
}
}
// return should_deallocate?
// 引用计数减 1,如果引用计数减到了 0,就将 block 置为 deallocating 状态
// 返回值是 block 是否需要被 dealloc
static bool latching_decr_int_should_deallocate(volatile int32_t *where) {
while (1) {
int32_t old_value = *where;
// 如果引用计数还是满的,就不能 dealloc,#疑问:引用计数满了以后就不能减了么
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
return false; // latched high
}
// 如果引用计数为 0,按照正常的逻辑,它应该已经被置为 deallocating 状态,不需要再被 dealloc,所以返回 false
if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
return false; // underflow, latch low
}
int32_t new_value = old_value - 2; // 引用计数减
bool result = false;
// 如果 old_value 在 0~15 位的值是 0b10,即引用计数是 1,且不是 deallocating 状态
if ((old_value & (BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING)) == 2) {
// old_value 减 1 后,新值 new_value 在 0~15 位值是 0b01,即引用计数变为0,且 BLOCK_DEALLOCATING 位变为 1
new_value = old_value - 1;
// 需要被 dealloc
result = true;
}
// 将新值 new_value 放入 where 中
if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
return result;
}
}
}
/**************************************************************************
Framework callback functions and their default implementations.
***************************************************************************/
#if !TARGET_OS_WIN32
#pragma mark Framework Callback Routines
#endif
static void _Block_retain_object_default(const void *ptr __unused) { }
static void _Block_release_object_default(const void *ptr __unused) { }
static void _Block_destructInstance_default(const void *aBlock __unused) {}
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
static void (*_Block_destructInstance) (const void *aBlock) = _Block_destructInstance_default;
/**************************************************************************
Callback registration from ObjC runtime and CoreFoundation
***************************************************************************/
void _Block_use_RR2(const Block_callbacks_RR *callbacks) {
_Block_retain_object = callbacks->retain;
_Block_release_object = callbacks->release;
_Block_destructInstance = callbacks->destructInstance;
}
/****************************************************************************
Accessors for block descriptor fields
*****************************************************************************/
#if 0
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
return aBlock->descriptor;
}
#endif
// 取得 block 中的 Block_descriptor_2,它藏在 descriptor 列表中
// 调用者:_Block_call_copy_helper() / _Block_call_dispose_helper
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
// Block_descriptor_2 中存的是 copy/dispose 方法,如果没有指定有 copy / dispose 方法,则返回 NULL
if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
// 先取得 Block_descriptor_1 的地址
uint8_t *desc = (uint8_t *)aBlock->descriptor;
// 偏移 Block_descriptor_1 的大小,就是 Block_descriptor_2 的起始地址
desc += sizeof(struct Block_descriptor_1);
return (struct Block_descriptor_2 *)desc;
}
// 取得 block 中的 Block_descriptor_3,它藏在 descriptor 列表中
// 调用者:_Block_extended_layout() / _Block_layout() / _Block_signature()
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
// Block_descriptor_3 中存的是 block 的签名,如果没有指定有签名,则直接返回 NULL
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
// 先取得 Block_descriptor_1 的地址
uint8_t *desc = (uint8_t *)aBlock->descriptor;
// 先偏移 Block_descriptor_1 的大小
desc += sizeof(struct Block_descriptor_1);
// 如果还有 Block_descriptor_2,就再偏移 Block_descriptor_2 的大小,得到的就是 Block_descriptor_3 的地址
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
// 调用 block 的 copy helper 方法,即 Block_descriptor_2 中的 copy 方法
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
// 取得 block 中的 Block_descriptor_2
struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
// 如果没有 Block_descriptor_2,就直接返回
if (!desc) return;
// 调用 desc 中的 copy 方法,copy 方法中会调用 _Block_object_assign 函数
(*desc->copy)(result, aBlock); // do fixup
}
// 调用 block 的 dispose helper 方法,即 Block_descriptor_2 中的 dispose 方法
static void _Block_call_dispose_helper(struct Block_layout *aBlock)
{
// 取得 block 中的 Block_descriptor_2
struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
// 如果没有 Block_descriptor_2,就直接返回
if (!desc) return;
// 调用 desc 中的 dispose 方法,dispose 方法中会调用 _Block_object_dispose 函数
(*desc->dispose)(aBlock);
}
/*******************************************************************************
Internal Support routines for copying
********************************************************************************/
#if !TARGET_OS_WIN32
#pragma mark Copy/Release support
#endif
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
// 拷贝 block,
// 如果原来就在堆上,就将引用计数加 1;
// 如果原来在栈上,会拷贝到堆上,引用计数初始化为 1,并且会调用 copy helper 方法(如果存在的话);
// 如果 block 在全局区,不用加引用计数,也不用拷贝,直接返回 block 本身
// 参数 arg 就是 Block_layout 对象,
// 返回值是拷贝后的 block 的地址
// 运行?stack -》malloc
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
// 如果 arg 为 NULL,直接返回 NULL
if (!arg) return NULL;
// The following would be better done as a switch statement
// 强转为 Block_layout 类型
aBlock = (struct Block_layout *)arg;
const char *signature = _Block_descriptor_3(aBlock)->signature;
// 如果现在已经在堆上
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
// 就只将引用计数加 1
latching_incr_int(&aBlock->flags);
return aBlock;
}
// 如果 block 在全局区,不用加引用计数,也不用拷贝,直接返回 block 本身
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {
// Its a stack block. Make a copy.
// block 现在在栈上,现在需要将其拷贝到堆上
// 在堆上重新开辟一块和 aBlock 相同大小的内存
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);
// 开辟失败,返回 NULL
if (!result) return NULL;
// 将 aBlock 内存上的数据全部复制新开辟的 result 上
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#endif
// reset refcount
// 将 flags 中的 BLOCK_REFCOUNT_MASK 和 BLOCK_DEALLOCATING 部分的位全部清为 0
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
// 将 result 标记位在堆上,需要手动释放;并且引用计数初始化为 1
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
// copy 方法中会调用做拷贝成员变量的工作
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
// isa 指向 _NSConcreteMallocBlock
result->isa = _NSConcreteMallocBlock;
return result;
}
}
// Runtime entry points for maintaining the sharing knowledge of byref data blocks.
// A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
// Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
// We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
// Otherwise we need to copy it and update the stack forwarding pointer
// 1. 如果 byref 原来在堆上,就将其拷贝到堆上,拷贝的包括 Block_byref、Block_byref_2、Block_byref_3,
// 被 __weak 修饰的 byref 会被修改 isa 为 _NSConcreteWeakBlockVariable,
// 原来 byref 的 forwarding 也会指向堆上的 byref;
// 2. 如果 byref 已经在堆上,就只增加一个引用计数。
// 参数 dest是一个二级指针,指向了目标指针,最终,目标指针会指向堆上的 byref
static struct Block_byref *_Block_byref_copy(const void *arg) {
// arg 强转为 Block_byref * 类型
struct Block_byref *src = (struct Block_byref *)arg;
// 引用计数等于 0
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
// 为新的 byref 在堆中分配内存
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
// 新 byref 的 flags 中标记了它是在堆上,且引用计数为 2。
// 为什么是 2 呢?注释说的是 non-GC one for caller, one for stack
// one for caller 很好理解,那 one for stack 是为什么呢?
// 看下面的代码中有一行 src->forwarding = copy。src 的 forwarding 也指向了 copy,相当于引用了 copy
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// 堆上 byref 的 forwarding 指向自己
copy->forwarding = copy; // patch heap copy to point to itself
// 原来栈上的 byref 的 forwarding 现在也指向堆上的 byref
src->forwarding = copy; // patch stack to point to heap copy
// 拷贝 size
copy->size = src->size;
// 如果 src 有 copy/dispose helper
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
// 取得 src 和 copy 的 Block_byref_2
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
// copy 的 copy/dispose helper 也与 src 保持一致
// 因为是函数指针,估计也不是在栈上,所以不用担心被销毁
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
// 如果 src 有扩展布局,也拷贝扩展布局
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
// 没有将 layout 字符串拷贝到堆上,是因为它是 const 常量,不在栈上
copy3->layout = src3->layout;
}
// 调用 copy helper,因为 src 和 copy 的 copy helper 是一样的,所以用谁的都行,调用的都是同一个函数
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
// 如果 src 没有 copy/dispose helper
// 将 Block_byref 后面的数据都拷贝到 copy 中,一定包括 Block_byref_3
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// already copied to heap
// src 已经在堆上,就只将引用计数加 1
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
// 对 byref 对象做 release 操作,
// 堆上的 byref 需要 release,栈上的不需要 release,
// release 就是引用计数减 1,如果引用计数减到了 0,就将 byref 对象销毁
static void _Block_byref_release(const void *arg) {
struct Block_byref *byref = (struct Block_byref *)arg;
// dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
// 取得真正指向的 byref,如果 byref 已经被堆拷贝,则取得是堆上的 byref,否则是栈上的,栈上的不需要 release,也没有引用计数
byref = byref->forwarding;
// byref 被拷贝到堆上,需要 release
if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
// 取得引用计数
int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
os_assert(refcount);
// 引用计数减 1,如果引用计数减到了 0,会返回 true,表示 byref 需要被销毁
if (latching_decr_int_should_deallocate(&byref->flags)) {
// 如果 byref 有 dispose helper,就先调用它的 dispose helper
if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
// dispose helper 藏在 Block_byref_2 里
(*byref2->byref_destroy)(byref);
}
free(byref);
}
}
}
/************************************************************
*
* API supporting SPI
* _Block_copy, _Block_release, and (old) _Block_destroy
* API - Application Programming Interface
* API 和 SPI 都是相对的概念,他们的差别只在语义上,API 直接被应用开发人员使用,SPI 被框架扩张人员使用
***********************************************************/
#if !TARGET_OS_WIN32
#pragma mark SPI/API
#endif
// API entry point to release a copied Block
// 对 block 做 release 操作。
// block 在堆上,才需要 release,在全局区和栈区都不需要 release.
// 先将引用计数减 1,如果引用计数减到了 0,就将 block 销毁
void _Block_release(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
// 如果 block == nil
if (!aBlock) return;
// 如果 block 在全局区
if (aBlock->flags & BLOCK_IS_GLOBAL) return;
// block 不在堆上
if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
// 引用计数减 1,如果引用计数减到了 0,会返回 true,表示 block 需要被销毁
if (latching_decr_int_should_deallocate(&aBlock->flags)) {
// 调用 block 的 dispose helper,dispose helper 方法中会做诸如销毁 byref 等操作
_Block_call_dispose_helper(aBlock);
// _Block_destructInstance 啥也不干,函数体是空的
_Block_destructInstance(aBlock);
free(aBlock);
}
}
// 尝试 retain block。当 block 不是处于 dealloc 时,引用计数加 1
// 返回值是是否成功,只有在 block 处于 deallocating 时,才会失败
bool _Block_tryRetain(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
return latching_incr_int_not_deallocating(&aBlock->flags);
}
// 判断 block 是否处于 deallocating 状态
bool _Block_isDeallocating(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
return (aBlock->flags & BLOCK_DEALLOCATING) != 0;
}
/************************************************************
*
* SPI used by other layers
*
***********************************************************/
// 取得 block 的完整大小
size_t Block_size(void *aBlock) {
return ((struct Block_layout *)aBlock)->descriptor->size;
}
// 如果 block 的返回值在栈上,则返回 TRUE,反之返回 FALSE
bool _Block_use_stret(void *aBlock) {
struct Block_layout *layout = (struct Block_layout *)aBlock;
// block 的 flag 有 BLOCK_HAS_SIGNATURE 和 BLOCK_USE_STRET,才会返回 TRUE
int requiredFlags = BLOCK_HAS_SIGNATURE | BLOCK_USE_STRET;
return (layout->flags & requiredFlags) == requiredFlags;
}
// Checks for a valid signature, not merely the BLOCK_HAS_SIGNATURE bit.
// 判断 block 是否有签名,不判断 BLOCK_HAS_SIGNATURE,而是通过直接取签名字符串来确定存在与否
bool _Block_has_signature(void *aBlock) {
return _Block_signature(aBlock) ? true : false;
}
// 取得 block 的签名字符串,可能是 NULL
const char * _Block_signature(void *aBlock)
{
struct Block_layout *layout = (struct Block_layout *)aBlock;
struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
// 如果没有 desc3,则一定没有签名,返回 NULL
if (!desc3) return NULL;
return desc3->signature;
}
block 归纳总结
- 如果block已经在堆上计数+1,返回block。
- 如果block在全局flags & BLOCK_IS_GLOBAL直接返回block本身。
- 如果block在栈上,需要拷贝block到堆上(重新开辟一个内存地址)result上,最后result->isa = _NSConcreteMallocBlock;指向了_NSConcreteMallocBlock 堆类型。
堆block,栈block,全局block对于这三种类型的block遵循俩个原则:
- block如果没有使⽤外部变量,或者只使⽤静态变量和全局变量,那⼀定是全局blcok。
- block如果使⽤了外部变量,⽽且不是静态变量或全局变量,如果赋值给强引⽤的是堆block,如果赋值给弱引⽤的是栈blcok。(所以栈blcok,可以接收外部参数)