[toc]
參考
https://blog.csdn.net/u014600626/article/details/78697535
http://www.cocoachina.com/cms/wap.php?action=article&id=23147
http://www.reibang.com/p/d96d27819679
block 簡(jiǎn)介
block 是能夠捕獲外部變量的匿名函數(shù), 在 iOS4 中引入, 是對(duì)C語(yǔ)言的擴(kuò)充, C語(yǔ)言本身不存在這樣的匿名函數(shù)决帖。
block 本質(zhì)
block 本質(zhì)上是封裝了 函數(shù)調(diào)用(函數(shù)指針) 以及 函數(shù)調(diào)用環(huán)境(捕獲到的參數(shù)) 的 OC對(duì)象呛伴。既然是OC對(duì)象, 那就是一個(gè)內(nèi)部有isa指針的結(jié)構(gòu)體。
block 屬性策略
MRC下block屬性的建議寫法
@property (copy, nonatomic) void (^block)(void);
ARC下block屬性的建議寫法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
代碼存放在堆區(qū)時(shí), 就需要特別注意, 因?yàn)槎褏^(qū)不像代碼區(qū)不變化, 堆區(qū)是不斷變化的(不斷創(chuàng)建銷毀)。因此當(dāng)沒有強(qiáng)指針指向時(shí), 代碼有可能會(huì)被銷毀, 如果這時(shí)再訪問(wèn)此段代碼則會(huì)程序崩潰向拆。
因此, 對(duì)于這種情況, 我們?cè)诙x一個(gè)block屬性時(shí)應(yīng)指定為strong, 或copy:
@property (nonatomic, strong) void (^myBlock)(void); // 這樣就有強(qiáng)指針指向它
@property (nonatomic, copy) void (^myBlock)(void);
block 相關(guān)源碼
Block.h
BLOCK_EXPORT void *_Block_copy(const void *aBlock);
BLOCK_EXPORT void _Block_release(const void *aBlock);
#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))
Block_private.h
// (非完整)
enum {
/* See function implementation for a more complete description of these fields and combinations */
BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), block, ... */
BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */
BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the __block variable */
BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy helpers */
BLOCK_BYREF_CALLER = 128 /* called from __block (byref) copy/dispose support routines. */
};
/* Runtime entry point called by compiler when assigning objects inside copy helper routines */
BLOCK_EXPORT void _Block_object_assign(void *destAddr, const void *object, const int flags);
/* BLOCK_FIELD_IS_BYREF is only used from within block copy helpers */
/* runtime entry point called by the compiler when disposing of objects inside dispose helper routine */
BLOCK_EXPORT void _Block_object_dispose(const void *object, const int flags);
runtime.c
/* Copy, or bump refcount, of a block. If really copying, call the copy helper if present. */
static void *_Block_copy_internal(const void *arg, const int flags) {
struct Block_layout *aBlock;
const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
//printf("_Block_copy_internal(%p, %x)\n", arg, flags);
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GC) {
// GC refcounting is expensive so do most refcounting here.
if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) {
// Tell collector to hang on this - it will bump the GC refcount version
_Block_setHasRefcount(aBlock, true);
}
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
// Its a stack block. Make a copy.
if (!isGC) {
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
result->isa = _NSConcreteMallocBlock;
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
else {
// Under GC want allocation with refcount 1 so we ask for "true" if wantsOne
// This allows the copy helper routines to make non-refcounted block copies under GC
unsigned long int flags = aBlock->flags;
bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0;
struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR);
if (!result) return (void *)0;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
// if we copy a malloc block to a GC block then we need to clear NEEDS_FREE.
flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK); // XXX not needed
if (wantsOne)
flags |= BLOCK_IS_GC | 1;
else
flags |= BLOCK_IS_GC;
result->flags = flags;
if (flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper...\n");
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
if (hasCTOR) {
result->isa = _NSConcreteFinalizingBlock;
}
else {
result->isa = _NSConcreteAutoBlock;
}
return result;
}
}
// API entry point to release a copied Block
void _Block_release(void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
int32_t newCount;
if (!aBlock) return;
newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;
if (newCount > 0) return;
// Hit zero
if (aBlock->flags & BLOCK_IS_GC) {
// Tell GC we no longer have our own refcounts. GC will decr its refcount
// and unless someone has done a CFRetain or marked it uncollectable it will
// now be subject to GC reclamation.
_Block_setHasRefcount(aBlock, false);
}
else if (aBlock->flags & BLOCK_NEEDS_FREE) {
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);
_Block_deallocator(aBlock);
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
;
}
else {
printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock);
}
}
block 相關(guān) struct
block 在 clang編譯后的結(jié)構(gòu)體:
struct __block_impl {
void *isa; // 指向block的具體類型
int Flags;
int Reserved;
void *FuncPtr; // 函數(shù)指針 FunctionPointer, 指向block的實(shí)現(xiàn) __funcName_block_func_0
};
block 在 Block_private.h 中的聲明:
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};