您的位置:首页 > 移动开发 > Objective-C

关于OC中的Block使用以及ARC和MAR下的内存管理方式

2016-07-25 15:19 447 查看
1.什么是Block?

Block是属于C语言框架

Block是一种数据类型(类似int)

Block是一段代码块,只有在被调用的时候被执行(类似函数和方法)

Block可以定义成临时变量

Block可以定义成参数

Block可以定义成属性

2.Block的语法结构

1.无参数,无返回值
/*

数据类型 : void(^)()

变量名   : task1

值   : ^{ NSLog(@"task1"); };

*/

void(^task1)() = ^{

NSLog(@"task1");

};

[/code]

2.有参数,无返回值

//参数类型(如果有形参,这个可省)形参

void(^task2)(NSString *) = ^(NSString *str){

NSLog(@"task2 %@",str);

};

[/code]

3.无参数,有返回值
int(^task3)() = ^{

return 1;

};

[/code]

4.有参数,有返回值

int(^task4)() = ^(int a,int b){

return a + b;

};

[/code]

3.ARC下,Block的内存空间

结论:

1.ARC环境下,单纯的定义一个block存储在全局区 <__NSGlobalBlock__:
0x1045c60b0>

示例代码

- (void)blockDemo

{

void(^task1)() = ^{

NSLog(@"task");

};

task1();

// block的本质是指针对象

// ARC环境下,单纯的定义一个block存储在全局/常量区 <__NSGlobalBlock__: 0x10e80f100>

NSLog(@"task1==%@",task1);

}

[/code]

控制台打印:

2016-06-27 08:55:51.263 Block使用[732:22600] task

2016-06-27 08:55:51.264 Block使用[732:22600] task1==<__NSGlobalBlock__: 0x10e80f100>

[/code]

4.ARC下,Block内访问外部变量的内存变化

结论:

前提是ARC : Block访问外部的变量

2.ARC环境下,block访问外部的变量时存储在堆区 <__NSMallocBlock__:
0x7fa8fd00c1b0>

2.1 在block访问这个变量之前,变量在栈区 ==
0x7fff53c73bfc

2.2 在block内部访问这个变量时,变量会被block拷贝到堆区 0x7fe851517880

示例代码:

#pragma mark - ARC - Block访问外部的变量

// 需求 : 研究block和外部变量的内存的变化

- (void)blockDemo1

{

int num = 10;

// 在block访问这个变量之前,变量在栈区 == 0x7fff52544bec

NSLog(@"num01==%p",&num);

void(^task1)() = ^{

// 在block内部访问这个变量时,变量会被block拷贝到堆区 0x7f8108e0d620

NSLog(@"task1 %d %p",num,&num);

};

task1();

// block的本质是指针对象

// ARC环境下,block访问外部的变量时存储在堆区 <__NSMallocBlock__: 0x7f8108e0d600>

NSLog(@"task1==%@",task1);


// 当block在其内部使用完了外部的变量之后,这个变量又会重新回到栈区 0x7fff52544bec

NSLog(@"num02==%p",&num);

}

[/code]

控制台打印日志:

2016-06-27 08:59:39.334 Block使用[753:25411] num01==0x7fff52544bec

2016-06-27 08:59:39.334 Block使用[753:25411] task1 10 0x7f8108e0d620

2016-06-27 08:59:39.334 Block使用[753:25411] task1==<__NSMallocBlock__: 0x7f8108e0d600>

2016-06-27 08:59:39.334 Block使用[753:25411] num02==0x7fff52544bec

[/code]

5.ARC下,Block内修改外部变量的内存变化

结论:

前提是ARC : Block修改外部的变量

1.ARC环境下,当block修改外部变量的时候,会在堆区 <__NSMallocBlock__:
0x7f82ac8a6130>

2.在block的外面,即使你使用__block修饰了,那么他的地址依然不变,在栈区 0x7fff5e101bf8

3.在block内部修改外部的变量时,使用__block修饰了外部的变量之后,外部的变量会在堆区0x7f9d10e0bcc8

示例代码

#pragma mark - ARC - Block修改外部的变量

// 需求 : 研究block和外部变量的内存的变化

- (void)blockDemo2

{

__block int num = 10;

// 在block的外面,即使你使用__block修饰了,那么他的地址依然不变,在栈区 0x7fff57337be8

NSLog(@"num01==%p",&num);

void(^task2)() = ^{

/*

 提示 : 在block内部修改外部变量是不被允许的

 如果非要修改,那么久需要把外部的变量用 __block 来修饰

*/

num = 20;

// 在block内部修改外部的变量时,使用__block修饰了外部的变量之后,外部的变量会在堆区 0x7fd3ea443ad8

NSLog(@"task2 %d =%p",num,&num);

};

task2();

// 当block修改外部变量的时候,会在堆区 <__NSMallocBlock__: 0x7fd3ea438d70>

NSLog(@"task2%@",task2);

// 当block内部修改完外部的变量之后,那么这个变量的就会保存到堆区 0x7fd3ea443ad8

NSLog(@"num02==%p",&num);

}

[/code]

控制台打印日志

2016-06-27 09:04:17.459 Block使用[773:28467] num01==0x7fff57337be8

2016-06-27 09:04:17.460 Block使用[773:28467] task2 20 =0x7fd3ea443ad8

2016-06-27 09:04:17.460 Block使用[773:28467] task2<__NSMallocBlock__: 0x7fd3ea438d70>

2016-06-27 09:04:17.460 Block使用[773:28467] num02==0x7fd3ea443ad8

[/code]

6.MRC下,Block的内存位置

结论:

1.MRC环境下,单纯的定义一个block存储在全局区 <__NSGlobalBlock__:
0x1045c60b0>

示例代码
- (void)blockDemo1

{

void(^task1)() = ^{

};


task1();


// 全局/常量区 <__NSGlobalBlock__: 0x10ddf10f0>

NSLog(@"%@",task1);

}

[/code]

控制台信息
2016-06-27 09:42:17.813 MRC下Block内存管理[973:45271] <__NSGlobalBlock__: 0x10ddf10f0>

[/code]

7.MRC下,Block的访问外部变量的内存变化

结论:

1.MRC环境下,Block访问外部变量的时候存放在栈区。 <__NSStackBlock__:
0x7fff58e23b08>

a.Block访问外部变量时,外部变量在栈区,地址 0x7fff58e23b3c

b.Block访问外部变量时,外部变量依然在栈区,但是地址发生了变化 0x7fff58e23b28

c.Block访问结束以后。外部变量依然在栈区,并且地址和原始的一致。

示例代码
- (void)blockDemo2

{

int num = 10;


// 变量的内存在栈区 0x7fff58e23b3c

NSLog(@"num01==%p",&num);


void(^task2)() = ^{


// 外部的变量在block内部依然在栈区,但是地址变了 0x7fff58e23b28 (地址变化了,内存空间没变)

NSLog(@"task2 %d %p",num,&num);

};

task2();


// block存储在栈区 <__NSStackBlock__: 0x7fff58e23b08>

NSLog(@"%@",task2);


// 变量依然在栈区,并且地址和开始时的一样 0x7fff58e23b3c

NSLog(@"num02==%p",&num);

}

[/code]

控制台信息

2016-06-27 09:47:52.118 MRC下Block内存管理[987:48208] num01==0x7fff58e23b3c

2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] task2 10 0x7fff58e23b28

2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] <__NSStackBlock__: 0x7fff58e23b08>

2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] num02==0x7fff58e23b3c

[/code]

8.MRC下,Block修改外部变量的内存变化

结论:

1.MRC环境下,Block修改外部变量时存放在栈区<__NSStackBlock__:
0x7fff57e2cae0>

a.Block修改外部变量前,外部变量存放在栈区

b.Block修改外部变量时,外部变量依然存放在栈区,但是地址发生了变化。

c.Block修改外部变量之后,外部变量的地址已经发生变化,已经不是原始值了。

示例代码
- (void)blockDemo3

{

__block int num = 10;


// 在block访问之前,外部变量存放在栈区 0x7fff57e2cb38

NSLog(@"num01==%p",&num);


void (^task3)() = ^{


num = 20;


//在block修改时,外部变量依然存放在栈区,但是地址发生了变化, 0x7fff57e2cb38

NSLog(@"task3 %d %p",num,&num);

};


task3();


// block的地址存放在栈区  <__NSStackBlock__: 0x7fff57e2cae0>

NSLog(@"%@",task3);


// 外部变量在block内部修改以后,地址为修改时的地址,依然存放在栈区 0x7fff57e2cb38

NSLog(@"num02==%p",&num);

}

[/code]

控制台信息
2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] num01==0x7fff57e2cb38

2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] task3 20 0x7fff57e2cb38

2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] <__NSStackBlock__: 0x7fff57e2cae0>

2016-06-27 10:00:01.070 MRC下Block内存管理[1006:53704] num02==0x7fff57e2cb38

[/code]

8.为什么Block属性需要使用copy修饰

a.在ARC下,使用strong和copy都是一样的,因为在访问/修改外部变量的时候,block都是在堆区,苹果官方建议使用copy

b.在MRC下,单纯的Block是存放在全局/常量区的,如果Block访问/修改外部变量后,block存放在了栈区,在栈区是不可以全局共享的,只有堆区的对象,变量才会被全局共享,所以使用copy拷贝一份Block到堆区中,这样Block才会全局共享。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IOS xcode objective-c