iOS与OS X之Block實現

基本語法:

   ^返回值類型 參數列表 表達式

其中返回值類型和參數列表均可省略!

使用Block語法將Block 賦值為 Block類型變量
int (^blk)(int) = ^(int count){ return count +1;};
由"^"開始的Block語法生成的Block被賦值給亦是blk中,因為與通常的變量相同,當然可以進行變量賦值
int (^blk1)(int) = blk;
int (^blk2)(int);
blk2 = blk1;

使用 typedef 簡化block代碼

typedef int (^blk_t)(int); //定義一個blk_t類型變量
//作用可作為參數
void func(blk_t blk){
}
//作為參數返回值
blk_t func(){
}

_block說明符 -- 在賦值的時候需要加上__block說明符

__block int val = 0; void (^blk)(void) = ^{val = 1;}; //只有加上__block,才可以在Block中修改值!

下面的代碼不是賦值,看起來只是使用數組,好像沒有總是,但運行會報錯!

    const char text[] = "hello";
    void ()(void) = ^{
        printf("%c\n",text[2]);
    };

這是因為在現在的Block中,並沒有實現對C語言數組的截獲,使用指針可以解決該問題

`const char *text = "hello";

Block的實質

通過 clang -rewrite-objc 源代碼文件名 可將Block轉換成C++源代碼

int main()
{
       void (^blk)(void) = ^{printf("Block\n");};
       blk();
       return 0;
}

通過轉換變成差不多有10萬行代碼,以下形式

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
...
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc; //由於轉換後源代碼中加入其構造函數,其實結構體就上面兩個變量

//初始化__main_block_impl_0結構體的構造函數
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {//__csself就是指的Block本身
printf("Block\n");}   //就是源碼中的^{printf("Block\n");};

 __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};//分配Block的空間

int main(int argc, const char * argv[]) {//主函數

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));//這行為將棧上生成的__main_block_impl_0結構體實例的指針賦值給指針變量blk
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);//去掉轉換部分(*blk->imp1.FuncPtr)(blk);這行為調用 blk();
    return 0;
}

  • __main_block_func_0 名稱的由來是因為block在main函數中且是第一次出現所以後面是0
  • 主函數main中構造函數的調用可簡化為->
  struct __main_block_imp1_0 tmp =  __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
    struct __main_block_imp1_0 *blk = &tmp;
  • isa = &_NSConcreteStackBlock的理解
typedef struct objc_object {
    Class isa;
} *id;
struct objc_class {
      Class isa;
};

假設通過NSObject生成的一個結構體實例中會通過變量isa保持該類的結構體實例指針,如下圖所示


iOS与OS X之Block實現_第1张图片
image.png
iOS与OS X之Block實現_第2张图片
image.png

所以_NSConcreteStackBlock就相當於class_t結構體實例,因為Block為OC的對象,關於該類的信息放置於_NSConcreteStackBlock中,於是將其賦值給isa指針!

Block的實質-在Block修改外面的值

    __block int val = 7;
    void (^blk)(void) = ^{val = 1;};

轉換後的代碼如下

struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) = 1; //通過__forwarding拿到__Block_byref_val_0實例的指針
}

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {

    __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 7};
    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));

    return 0;
}

  • 加了__block的變量變成了__Block_byref_val_0結構體實例,該實例的成員變量__forwarding指向該實例的指針,可通過此指針
    iOS与OS X之Block實現_第3张图片
    image.png

    所以上面賦值是這樣的形式--> (val->__forwarding->val) = 1

Block 存儲域

iOS与OS X之Block實現_第4张图片
image.png

Block類型分以下幾種

  • _NSConcreteStackBlock 設置在棧上的Block
  • _NSConcreteGlobalBlock 設置在數據區(.data區)
  • _NSConcreteMallocBlock malloc函數分配的內存塊(堆區)
iOS与OS X之Block實現_第5张图片
image.png
iOS与OS X之Block實現_第6张图片
image.png

以下源代碼生成並持有NSMutableArray類的對象,由於附有__strong修飾符的賦值變量作用域立即結束,因此對象被立即釋放並廢棄

    {
        id array = [[NSMutableArray alloc]init];
    }

我們來看一下Block語法中使用該變量array的代碼

int main(int argc, const char * argv[]) {
    typedef void (^blk_t)(id);

    blk_t blk;
    {
        id array = [[NSMutableArray alloc]init];//如果用__block id __weak array2 = array;然後下面用array2的話,會因為走出大括號後,array的強引用消失,對象不存在,將array2指針變為Nil,會導致大括號後的打印全是0
        blk = [^(id obj){
            [array addObject:obj];
            NSLog(@"array count = %ld",[array count]);
            
        }copy];
    }
    blk([[NSObject alloc]init]);
    blk([[NSObject alloc]init]);
    blk([[NSObject alloc]init]);
    

    return 0;
}

轉換後代碼如下:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id array;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _array, int flags=0) : array(_array) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {
  id array = __cself->array; // bound by copy

            ((void (*)(id, SEL, ObjectType))(void *)objc_msgSend)((id)array, sel_registerName("addObject:"), (id)obj);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_x0_xwklvzm967x54vp3bvrp8b2m0000gn_T_main_ea6c83_mi_0,((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array, sel_registerName("count")));

        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); //retain函數的實現在上面
  void (*dispose)(struct __main_block_impl_0*);//release函數的實現在上面
} 

__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    typedef int (*blk_t)(id);

    blk_t blk;
    {
        id array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
        blk = (blk_t)((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)(id))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344)), sel_registerName("copy"));


    }
    ((int (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));
    ((int (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));
    ((int (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));


    return 0;
}
  • _Block_object_assign 相當於retain實例方法的函數
  • _Block_object_dispose 相當於release實例方法的函數
    但是上面的兩個函數只是賦值給了結構體__main_block_desc_0的成員變量中,並沒有調用,原因如下


    iOS与OS X之Block實現_第7张图片
    image.png
iOS与OS X之Block實現_第8张图片
image.png

本以為在去掉copy後,因為變量的作用域結束會導致NSMutableArray對象也廢棄,但是通過運行後通過導出C++發現除了沒有調用copy,其他的都步驟都有做,所以才沒有導致崩潰!這是和書上不同的地方!!!

iOS与OS X之Block實現_第9张图片
image.png

還是將書上的說明貼出來,


iOS与OS X之Block實現_第10张图片
image.png

block循環引用

示例一:

如果在Block中使用附有__strong修飾符的對象類型自動變量,那麼當Block從棧複製到堆時,該對象為Block所持有,這樣容易引起循環引用

typedef void (^blk_t)(void);
@interface MyObject : NSObject
{
    blk_t blk_;
}

@end

@implementation MyObject

-(id)init{
    self = [super init];
    blk_ = ^{NSLog(@"self = %@",self);}; //self為帶有__strong修飾符的對象類型自動變量
    return self;
}

-(void)dealloc{
    NSLog(@"dealloc");
}

@end

int main(int argc, const char * argv[]) {
    
    id o = [[MyObject alloc]init];
    NSLog(@"%@",o);

    return 0;
}

循環救命如下圖


iOS与OS X之Block實現_第11张图片
image.png

為避免循環使用,可聲明附有__weak修飾符的變量,將self賦值使用

-(id)init{
    self = [super init];
//    blk_ = ^{NSLog(@"self = %@",self);};
    id __weak tmp = self;
    blk_ = ^{NSLog(@"self = %@",tmp);};
    return self;
}//這樣就會調用MyObject的dealloc釋放函數了

示例二:

typedef void (^blk_t)(void);
@interface MyObject : NSObject
{
    blk_t blk_;
}

@end

@implementation MyObject

-(id)init{
    self = [super init];

    __block id  tmp = self;
    blk_ = ^{NSLog(@"self = %@",tmp);
//        tmp = nil;
        };
    return self;
}

-(void)execBlock{
    blk_();
}

-(void)dealloc{
    NSLog(@"dealloc");
}

@end

int main(int argc, const char * argv[]) {
    
    id o = [[MyObject alloc]init];
    [o execBlock];
    return 0;
}

tmp = nil這句代碼屏蔽便會引起循環引用,因為

  • MyObject 類對象持有Block
  • Block持有__block對象
  • __block 變量持有MyObject類對象
    tmp=nil加上便可以解循環,如下圖所示
iOS与OS X之Block實現_第12张图片
image.png

你可能感兴趣的:(iOS与OS X之Block實現)