Block原理

Block就是一个存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体。

一、Block是一个对象。

#import <objc/runtime.h>
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    int i = 0;
    __unused void(^myBlock)() = ^{
        NSLog(@"block %d",i);
    };
    Class tmpClass = object_getClass(myBlock);
    while (tmpClass) {
        NSLog(@"class %@ superClass %@",tmpClass,class_getSuperclass(tmpClass));
        tmpClass = class_getSuperclass(tmpClass);
    }
}
@end

结果:

class __NSMallocBlock__ superClass __NSMallocBlock
class __NSMallocBlock superClass NSBlock
class NSBlock superClass NSObject
class NSObject superClass (null)

二、Block实现过程

1、通过命令转换:

//转换成C++代码命令:xcrun -sdk iphonesimulator8.1 clang -rewrite-objc ViewController.m
//包含weak类型数据转换成C++:xcrun -sdk iphonesimulator10.2 clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 ViewController.m
- (void)emptyBlockFunction {
    void (^emptyBlock)() = ^{
        NSLog(@"abc");
    };
    
    emptyBlock();
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};


// @implementation BlockStructureViewController
int globalValue = 10;
static int staticValue = 5;



struct __BlockStructureViewController__emptyBlockFunction_block_impl_0 {
  struct __block_impl impl;
  struct __BlockStructureViewController__emptyBlockFunction_block_desc_0* Desc;
  __BlockStructureViewController__emptyBlockFunction_block_impl_0(void *fp, struct __BlockStructureViewController__emptyBlockFunction_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockStructureViewController__emptyBlockFunction_block_func_0(struct __BlockStructureViewController__emptyBlockFunction_block_impl_0 *__cself) {


        NSLog((NSString *)&__NSConstantStringImpl__var_folders_fj_03n4zg_x4tv33bsws1sfxlkw0000gn_T_BlockStructureViewController_8a7742_mi_0);

    }

static struct __BlockStructureViewController__emptyBlockFunction_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA = { 0, sizeof(struct __BlockStructureViewController__emptyBlockFunction_block_impl_0)};

static void _I_BlockStructureViewController_emptyBlockFunction(BlockStructureViewController * self, SEL _cmd) {

    void (*emptyBlock)() = ((void (*)())&__BlockStructureViewController__emptyBlockFunction_block_impl_0((void *)__BlockStructureViewController__emptyBlockFunction_block_func_0, &__BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA));

    ((void (*)(__block_impl *))((__block_impl *)emptyBlock)->FuncPtr)((__block_impl *)emptyBlock);
    
    //这里调用结构体的构造函数(函数指针,desc的结构体变量)
    *emptyBlock = &__BlockStructureViewController__emptyBlockFunction_block_impl_0((void *)__BlockStructureViewController__emptyBlockFunction_block_func_0, &__BlockStructureViewController__emptyBlockFunction_block_desc_0_DATA))
    emptyBlock->FuncPtr(emptyBlock);
}

2、自己实现block

struct block {
    
    int Flags;
    void *FuncPtr;
    
};


struct block_impl {
    
    struct block impl;
    
    block_impl(void *fp, int flags = 0){

        impl.Flags = flags;
        impl.FuncPtr = fp;
        
    }
};
static void block_impl_func_0(struct block_impl *__cself) {
    printf("哈哈");
}

void test() {

    struct block_impl blockImpl = block_impl((void *)block_impl_func_0, 20);
    block *tmpBlockPointer = (block *)&blockImpl;
    
    //struct本身是不占内存
    void (*Func)(struct block *) = (void (*)(struct block *))tmpBlockPointer->FuncPtr;
    Func(tmpBlockPointer);
}

三、__block

- (void)blockDataBlockFunction {
    __block int a = 100;
    //代码会生成如下
    /*
    struct __Block_byref_a_0 {
      void *__isa;
      struct __Block_byref_a_0 *__forwarding;
      int __flags;
      int __size;
      int a;
     };
    */
    //放到栈里,是系统来控制内存的,离开作用域后,就释放内存了
    //OC里的对象都是在堆区的,堆区的内存需要你自己创建、自己释放
    void (^blockDataBlock)() = ^{        
        //堆栈区
        NSLog(@"block_int_a %p", &a);
        NSLog(@"a = %d", a);        
    };    
  //a->forwarding-a;
    
    //你在栈里的a->forwarding = block里的a的地址
    
    //你刚刚说外面的a是栈里面, block里面的a是在堆里面,那为什么我在外面改变a,能改变里面的值?
    //栈区修改a的值
    a = 10; //此处能在block里面修改a的值 实际上代码里面执行的是a->forwarding.a的值 
    blockDataBlock();
}
    int a = 10;
    void(^block)() = ^{
        NSLog(@"%d",a);
    };
    a = 0;//此处不能修改a的值
    block();

四、block存储的问题

typedef void(^testBlock)();
typedef void(^testBlock1)(int a);
@property(nonatomic, strong)testBlock strongBlock;
@property(nonatomic, copy)testBlock copyBlock;
@property(nonatomic, weak)testBlock weakBlock;

@property(nonatomic, strong)testBlock1 strongBlock1;
@property(nonatomic, copy)testBlock1 copyBlock1;
@property(nonatomic, weak)testBlock1 weakBlock1;

1、当你没有使用外部变量的时候,通通都是global的,无论你怎么修饰、无论是否成员变量;存储在代码区

    _strongBlock = ^{
        NSLog(@"_strongBlock");
    };
    _copyBlock = ^{
        NSLog(@"_copyBlock");
    };
    _weakBlock = ^{
        NSLog(@"_weakBlock");
    };
    NSLog(@"strongBLock %@  copyBLock %@  weakBLock %@", object_getClass(_strongBlock), object_getClass(_copyBlock), object_getClass(_weakBlock));
结果:
strongBLock __NSGlobalBlock__  copyBLock __NSGlobalBlock__  weakBLock __NSGlobalBlock__

2、当在block里添加了外部变量,那么block的内存就会变成不一样;

int k = 0;
    
    _strongBlock = ^{
        NSLog(@"_strongBlock %d", k);
    };
    _copyBlock = ^{
        NSLog(@"_copyBlock %d", k);
    };
    _weakBlock = ^{
        NSLog(@"_weakBlock %d", k);
    };
    
    NSLog(@"strongBLock %@  copyBLock %@  weakBLock %@", object_getClass(_strongBlock), object_getClass(_copyBlock), object_getClass(_weakBlock));
结果:
strongBLock __NSMallocBlock__  copyBLock __NSMallocBlock__  weakBLock __NSStackBlock__

3、当block赋值给strong变量、(非weak的)成员变量,block为mallocBlock;

    int k = 0;
    _strongBlock1 = ^(int i){
        NSLog(@"_strongBlock1 : i=%d", k);
    };
    _copyBlock1 = ^(int i){
        NSLog(@"_copyBlock1 : i=%d", k);
    };
    _weakBlock1 = ^(int i){
        NSLog(@"_weakBlock1 : i=%d", k);
    };
    NSLog(@"strongBLock1 %@  copyBLock1 %@  weakBLock1 %@", object_getClass(_strongBlock1), object_getClass(_copyBlock1), object_getClass(_weakBlock1));
结果:
strongBLock1 __NSMallocBlock__  copyBLock1 __NSMallocBlock__  weakBLock1 __NSStackBlock__

4、当block为函数参数,block为stackBlock;

5、全局变量、静态变量、静态局部变量在block中的存储,block都为globalBlock

6、static变量无法加__block符的

7、对globalBlock 进行copy操作 依然是它本身地址 对mallocBlock进行copy,引用计数+1 对stackBlock进行copy 地址改变了

8、需要手动对block进行copy的情况:作为函数参数,这里涉及到block数组,原因是栈里的对象,你无法来讲引用计数

  • 结论:
当你的block里有外部变量的时候

当 block 调用 copy 方法时,如果 block 在栈上,会被拷贝到堆上;

当 block 作为函数返回值返回时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;

当 block 被赋值给 __strong id 类型的对象或 (非weak)成员变量时,编译器自动将 block 作为 _Block_copy 函数,效果等同于 block 直接调用 copy 方法;

当 block 作为参数被传入方法名带有 usingBlock 的 Cocoa Framework 方法或 GCD 的 API 时。这些方法会在内部对传递进来的 block 调用 copy 或 _Block_copy 进行拷贝;

五、循环引用

typedef void(^cycleBlock)();
@interface ViewController (){
    cycleBlock blk;
    NSObject *object;
}

1、例一

    NSObject *otherObject = [[NSObject alloc] init];
    NSLog(@"object %p &object %p", otherObject, &otherObject);
    //otherObject本来是在堆区,然后把block赋值给成员对象,block会自动copy,到堆区里面,otherObject本来在堆区,(没有weak的情况下,就会对它有引用计数+1)
    __weak typeof(otherObject)weakObject = otherObject;
    blk = ^{
        NSLog(@"object %p &object %p", weakObject, &weakObject);
    };
    blk();
    //block 对otherObject不进行引用计数 此操作otherObject被释放掉
    otherObject = nil;
    blk();
    /*
    结果:
  object 0x60400000a630 &object 0x7fff5f32fc98
  object 0x60400000a630 &object 0x600000441160
  object 0x0 &object 0x600000441160
    */
    NSObject *otherObject = [[NSObject alloc] init];
    NSLog(@"object %p &object %p", otherObject, &otherObject);
    blk = ^{
        NSLog(@"object %p &object %p", otherObject, &otherObject);
    };
    blk();
    //block 对otherObject进行引用计数 此操作otherObject不会被释放
    otherObject = nil;
    blk();
    /*
    结果:
    object 0x6080000149f0 &object 0x7fff57690c98
    object 0x6080000149f0 &object 0x60800024bb40
    object 0x6080000149f0 &object 0x60800024bb40
    */

2、例二

__strong讲解: https://www.jianshu.com/p/404e0ea5f6d7

     NSMutableDictionary *otherObject = [[NSMutableDictionary alloc] init];
    NSLog(@"object %p &object %p", otherObject, &otherObject);
    __weak typeof(otherObject)weakObject = otherObject;
    blk = ^{
        __strong typeof(weakObject)strongObject = weakObject;
        NSLog(@"object %p &object %p", strongObject, &strongObject);
        //如果[otherObject setValue:@"sda" forKey:@"12"]; 则产生循环引用
        [strongObject setValue:@"sda" forKey:@"12"];
    };
    
    blk();
    otherObject = nil;
    blk();
    /*
    结果:
    object 0x60c000222480 &object 0x7fff51adcca8
    object 0x60c000222480 &object 0x7fff51adcc08
    object 0x0 &object 0x7fff51adcc08
    */

3、例三,

    NSObject *otherObject = [[NSObject alloc] init];
    NSLog(@"object %p &object %p", otherObject, &otherObject);
    
    __weak typeof(otherObject)weakObject = otherObject;
    
    //只要 block 部分执行了,即使我们中途释放了 obj,block 内部依然会继续强引用它
    
    blk = ^{
        // __strong会保证otherObject到运行结束前都不会被释放
        __strong typeof(weakObject)strongObject = weakObject;
        NSLog(@"object %p &object %p", strongObject, &strongObject);
        sleep(3);
        NSLog(@"object %p &object %p", strongObject, &strongObject);
    };
    
    blk();
    otherObject = nil;
    sleep(5);
    blk();
/*
运行结果:
object 0x608000010780 &object 0x7fff59c14ca8
object 0x608000010780 &object 0x7fff59c14c08
object 0x608000010780 &object 0x7fff59c14c08
object 0x0 &object 0x7fff59c14c08
object 0x0 &object 0x7fff59c14c08
*/

4、例四,对成员变量的循环引用

- (void)strongCycleBlockFunction {
    
    object = [[NSObject alloc] init];
    NSLog(@"object %p &object %p", object, &object);

    //对于成员变量,block是把self引用计数+1,不是对成员变量object本身来增加引用计数的
    blk = ^{
        
        NSLog(@"object %p &object %p", object, &object);
        
    };
    blk();
    //这里的object和block中的block是同一个内存地址
    object = nil;
    blk();
  /*
结果:因为block不是对objec进行引用的,而是对self进行引用
 object 0x600000010180 &object 0x7fd7426061e8
 object 0x600000010180 &object 0x7fd7426061e8
 object 0x0 &object 0x7fd7426061e8
  */
}

5、例五、block里面定义静态变量或者使用外部的静态变量

不需要对静态变量进行任何修饰,不存在循环引用。

static int i = 0;
@interface SecondController ()

@end

@implementation SecondController
- (void)dealloc{
    NSLog(@"%s",__func__);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    void (^test)() = ^{
        static int j = 0;
        NSLog(@"i = %d, j = %d", i,j);
    };
    i = 2;
    test();
}

6、例六

@interface SecondController ()
@property (nonatomic, copy) void(^blk)(NSString * str);
@end

@implementation SecondController{
    NSString * str;
}
- (void)dealloc{
    NSLog(@"%s",__func__);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    str = @"sjdk";
    self.view.backgroundColor = [UIColor whiteColor];
    self.blk = ^(NSString * str2){
        //self 持有 block ,block 持有str ,str被self持有,这里会产生循环引用。
        str = str2;
        NSLog(@"%@",str);
    };
    self.blk(@"21");
}
@end

如下写法是不存在循环引用的

@interface SecondController ()

@end

@implementation SecondController{
    NSString * str;
}
- (void)dealloc{
    NSLog(@"%s",__func__);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    str = @"sjdk";
    self.view.backgroundColor = [UIColor whiteColor];
    void(^blk)(NSString *) = ^(NSString * str2){
        str = str2;
        NSLog(@"%@",str);
    };
    blk(@"21");
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 《Objective-C高级编程》这本书就讲了三个东西:自动引用计数、block、GCD,偏向于从原理上对这些内容...
    WeiHing阅读 9,780评论 10 69
  • Block实际上是Objective-C对闭包的实现。 关于闭包的概念: In programming langu...
    chushen61阅读 338评论 0 0
  • Block是什么? Block实际上是Objective-C对闭包的实现。 关于闭包的概念:In programm...
    Gekkko阅读 1,416评论 0 12
  • 1 基本说明 Block一直是OC的一个重点、难点、黑科技。Block在日常项目中经常使用,他的实现方式和一般的o...
    NS西北风阅读 498评论 0 3
  • 记着《向着光亮那方》中说“看着一个人在一直得变好,那种感觉比自己好起来还要好。” “每一天快乐我的美少女” 呈现,...
    你就在对岸等我勇敢阅读 900评论 0 2