iOS之Block详解

简介:

一、Block定义


二、Block原理

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 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++代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
.......
 
__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地址,也就是两个匿名函数的入口地址。

?
1
2
3
4
5
6
7
8
9
10
11
#import <Foundation/Foundation.h>
 
@interface BlockTest : NSObject
 
@property (nonatomic, copy)  void  (^copyBlock)();
 
@property (nonatomic, weak)  void  (^weakBlock)();
 
- ( void )run;
 
@end
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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;
}
?
1
2
3
4
5
6
7
8
9
// 运行结果:
 
(++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下:

?
1
@property (nonatomic, copy)  void  (^testBlock)();
?
1
2
3
4
5
Person *p = [[Person alloc] init];
__weak typeof (p) weakP = p;         // 或:   __unsafe_unretained typeof(p) weakP = p;
p.testBlock = ^{
     [weakP run];
};

2、MRC下:

?
1
@property (nonatomic, copy)  void  (^testBlock)();
?
1
2
3
4
5
6
7
Person *p = [[Person alloc] init];
__block typeof (p) weakP = p;
p.testBlock = ^{
     [weakP run];
};
     
[p release];

目录
相关文章
|
iOS开发
iOS block修饰符用copy还是strong
iOS block修饰符用copy还是strong
144 0
|
iOS开发 Python
iOS小技能:lldb打印block参数签名
iOS逆向时经常会遇到参数为block类型,本文介绍一个lldb script,可快速打印出Objective-C方法中block参数的类型。
199 0
iOS小技能:lldb打印block参数签名
|
iOS开发 开发者
iOS开发 - 如何写出漂亮的block
iOS开发 - 如何写出漂亮的block
104 0
|
iOS开发
iOS开发- 关于Block的几种应用
iOS开发- 关于Block的几种应用
117 0
|
自然语言处理 iOS开发
IOS——Block
IOS——Block
79 0
|
iOS开发
iOS代理 通知 block传值的规范写法
iOS代理 通知 block传值的规范写法
145 0
|
存储 API iOS开发
iOS Principle:Block(下)
iOS Principle:Block(下)
131 0
iOS Principle:Block(下)
|
C语言 iOS开发 C++
iOS Principle:Block(上)
iOS Principle:Block(上)
115 0
iOS Principle:Block(上)
|
存储 中间件 Java
iOS-底层原理 30:Block底层原理
iOS-底层原理 30:Block底层原理
282 0
iOS-底层原理 30:Block底层原理
|
机器学习/深度学习 C++ iOS开发
iOS - __block 修饰符底层探索
Block技术合集 iOS - Block变量截获 Block的写法及使用
iOS - __block 修饰符底层探索