Block原理

2018-03-04  本文已影响19人  前年的邂逅_Jerry

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
上一篇下一篇

猜你喜欢

热点阅读