iOS之Block详解

一、Block定义

iOS之Block详解_第1张图片


二、Block原理

// main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
//        static int age = 20;
        __block int age = 20;
        
        void (^blcok)(void) = ^ {  
            age = 21;
            NSLog(@"%d", age);
        };
        
        age = 22;
        NSLog(@"%d", age);
        
        blcok();
        NSLog(@"%@", blcok);
    }
}

    将上述代码使用命令:$ clang -rewrite-objc main.m 编译后,截取C++代码如下:

.......

__attribute__((visibility("default"))) __attribute__((availability(macosx,introduced=10_8)))

#ifndef _REWRITER_typedef_NSXPCListenerEndpoint
#define _REWRITER_typedef_NSXPCListenerEndpoint
typedef struct objc_object NSXPCListenerEndpoint;
typedef struct {} _objc_exc_NSXPCListenerEndpoint;
#endif

struct NSXPCListenerEndpoint_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	void *_internal;
};

/* @end */


struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

// __main_block_impl_0(命名:main函数下,第0个名叫block的函数(impl))
// __main_block_impl_0:包含三个成员变量和一个构造函数
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
    // 定义一个__main_block_impl_0构造函数并初始化
    // 参数列表有__main_block_impl_0的一个成员变量做形参传进去初始化, age(_age)构造函数给age赋值 _age
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__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_age_0 *age = __cself->age; // bound by ref


            (age->__forwarding->age) = 21;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_c3_j1lbjks553g1cm01rczynmhw0000gn_T_main_dc1432_mi_0, (age->__forwarding->age));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 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成员变量,并初始化*/ __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[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 


        __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 20};

        // 等号左边的(*blcok)(void)是一个函数指针,等号右边的(void (*)())表示返回一个函数指针**IMP,block函数名就是函数指针首地址
        // __main_block_impl_0接收前两个形参,flags=0形参自动设置
        // __main_block_impl_0结构体中定义的fp指针来调用__main_block_func_0函数
        // __main_block_impl_0结构体中定义的desc指针接收__main_block_desc_0_DATA地址来初始化desc,并赋值给Desc
        // 传入age的地址&,使block能动态获取age的值变化
        void (*blcok)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

        (age.__forwarding->age) = 22;
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_c3_j1lbjks553g1cm01rczynmhw0000gn_T_main_dc1432_mi_1, (age.__forwarding->age));

        // 解引用__block_impl中的FunPtr成员变量,即解引用fp,也就是调用__main_block_func_0函数,入参为(__block_impl *)类型的block
        ((void (*)(__block_impl *))((__block_impl *)blcok)->FuncPtr)((__block_impl *)blcok);
    }
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };


三、Block存储

        block的存储形态有三种:_NSStackBlock(栈)、_NSGlobalBlock(全局)、_NSMallocBlock(堆)

 要点一:当block在函数内部,且定义的时候就使用了函数内部的变量,那么这个  block是存储在栈上的。

 要点二:当block定义在函数体外面,或者定义在函数体内部且当时函数执行的时候,block体中并没有需要使用函数内部的局部变量时,也就是block在函数执行的时候只是静静地待在一边定义了一下而不使用函数体的内容,那么block将会被编译器存储为全局block。

 要点三:全局block存储在堆中,对全局block使用copy操作会返回原函数指针;而对栈中的block使用copy操作,会产生两个不同的block地址,也就是两个匿名函数的入口地址。

#import <Foundation/Foundation.h>

@interface BlockTest : NSObject

@property (nonatomic, copy) void (^copyBlock)();

@property (nonatomic, assign) void (^weakBlock)();

- (void)run;

@end

typedef int (^blockSave)(void);

typedef void (^typedefBlock)(void);

void (^outFuncBlock)(void) = ^{
    NSLog(@"someBlock");
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {

#pragma mark - ARC机制优化会将stack(栈)的block,转为heap(堆)的block进行调用。
        
        __block int age = 20;
        int *ptr = &age;
        blockSave x = ^{
            NSLog(@"(++age):%d", ++age);
            return age;
        };
        blockSave y = [x copy];
        y();
        NSLog(@"(*ptr):%d", *ptr);
        /**
         ARC下:(++age):21   (*ptr):21
         MRC下:(++age):21   (*ptr):20
         */
        
#pragma mark - copyBlock (使用函数内变量)
        
        BlockTest *test = [[BlockTest alloc] init];
        
        test.copyBlock = ^{
            [test run];
        };
        NSLog(@"%@", test.copyBlock);
        
#pragma mark - copyBlock(未使用函数内变量)
        
        test.copyBlock = ^{
            NSLog(@"copyBlock");
        };
        NSLog(@"%@", test.copyBlock);
        
#pragma mark - weakBlock(使用函数内变量)
        
        test.weakBlock = ^{
            NSLog(@"weakBlock");
        };
        NSLog(@"%@", test.weakBlock);
 
#pragma mark - weakBlock(未使用函数内变量)
        
        test.weakBlock = ^{
            [test run];
        };
        NSLog(@"%@", test.weakBlock);
        
#pragma mark - someBlock(定义在函数体外)
        
        NSLog(@"%@", outFuncBlock);
        
#pragma mark - typedefBlock(函数体外自定义的Block)
        
        typedefBlock b = ^{
            NSLog(@"typedefBlock");
        };
    }
    return 0;
}

// 运行结果:

(++age):21
(*ptr):21
<__NSMallocBlock__: 0x100105960>
<__NSGlobalBlock__: 0x100002190>
<__NSGlobalBlock__: 0x1000021d0>
<__NSStackBlock__: 0x7fff5fbff6b8>
<__NSGlobalBlock__: 0x1000020c0>


四、Block解决循环引用做法

     1)如果没有对block进行copy操作,block就存储于栈空间;

     2)如果对block进行copy操作,block就存储于堆空间;

     3)如果block存储于栈空间,不会对block内部所用到的对象产生强引用;

     4)如果block存储于堆空间,就会对block内部所用到的对象产生强引用.

1、ARC下:

@property (nonatomic, copy) void (^testBlock)();

Person *p = [[Person alloc] init];
__weak typeof (p) weakP = p;        // 或:   __unsafe_unretained typeof(p) weakP = p;
p.testBlock = ^{
    [weakP run];
};

2、MRC下:

@property (nonatomic, copy) void (^testBlock)();

Person *p = [[Person alloc] init];
__block typeof (p) weakP = p;
p.testBlock = ^{
    [weakP run];
};
    
[p release];


你可能感兴趣的:(iOS之Block详解)