开发者社区> 墨云天> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

iOS之Block详解

简介:
+关注继续查看

一、Block定义

205854_ud4p_580523.png


二、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 开发之 Fucking Block Syntax !
How Do I Declare A Block in Objective-C? As a local variable: returnType (^blockName)(parameterTypes) = ^returnType(parameters) {.
1100 0
iOS开发之利用Block逆向传值
在iOS开发之通过代理逆向传值一文中,分析了利用代理模式来逆向传值,其实还有一些其他的方式,如通知、Block等,相比较代理,我个人认为反而要简单些,但是需要处理好细节问题,如Block循环引用。
555 0
iOS开发网络篇—发送json数据给服务器以及多值参数
iOS开发网络篇—发送json数据给服务器以及多值参数 一、发送JSON数据给服务器 发送JSON数据给服务器的步骤: (1)一定要使用POST请求 (2)设置请求头 (3)设置JSON数据为请求体 代码示例: 1 #import "YYViewController.
510 0
IOS开发专题---转化解析JSON格式为字符格式(原生类库解析)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.
462 0
iOS开发:block死循环及__weak弱引用提前释放的问题解决
block死循环及__weak弱引用提前释放的问题解决
0 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Hardware-With-A-$10-SD-Card-Reader
立即下载
“移”网打尽—Mobile Network as a Ser
立即下载
深入剖析 iOS 性能优化
立即下载