iOS_block问题

前言:

最近遇到了一个block的循环引用的问题,才发现我对block还一知半解,为此,对自己的理解做了一下正整理。

目录:

1、什么是block ?有什么作用?
2、block为什么使用copy修饰?
3、block为什么会造成循环引用?怎么解决?
4、block中基本数据类型,数值无法更改。

1、什么是block?有什么作用?

代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block是一种特殊的数据类型,其可以正常定义变量、作为参数、作为返回值,特殊地,Block还可以保存一段代码,在需要的时候调用,目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调。存储的数据是一个函数体。使用Block,就可以像其他标准函数一样,传入参数,并得到返回值。

block的格式:
     a:Block的返回值类型,可以为空(void);
     b:Block对象名称,可以理解为变量名;
     ^:块的语法标记,声明b为一个Block对象; 
     c:第一个参数类型
     d:第二个参数类型
     name1,name2:参数名;
     {}:Block代码块的主题部分。
block的声明:
例子:控制器A,控制器B,在A中点击button push到B,pop时从B传值到A。block为B中的属性和方法。
方式一:
typedef void(^testBlock)(void); // 定义类型,取别名
@property (nonatomic, copy) testBlock Block; // 再声明属性

实现:
B中:
_testBlock(@"1");

A中:
b.testBlock = ^(NSString *text) {
      nslog(@"%@",text);
 };

方式二:
@property (nonatomic, copy) void(^test1Block)(NSString *text); // 直接声明属性

实现:
B中:
_test1Block(@"1");

A中:
b.test1Block = ^(NSString *text) {
      nslog(@"%@",text);
 };

方式三:
-(void)testWithBlock:(void(^)(NSString *text))block; // 作为函数参数

实现:
B中:
- (void)testWithBlock:(void (^)(NSString *))block{
    block(@"1");
}

A中:
[a testWithBlock:^(NSString *text) {
        nslog(@"%@",text);
 }];
2、block为什么使用copy修饰?

a、block在创建的时候默认分配的内存是在栈上,而不是在堆上。这样的话其本身的作用域是属于创建时候的作用域,一旦在创建的作用域之外调用就会导致程序的崩溃。所以使用了copy将其拷贝到堆内存上。
b、block创建在栈上,而block的代码中可能会用到本地的一些变量,只有将其拷贝到堆上,才能用这些变量

3、block为什么会造成循环引用?怎么解决?

self 与 block 相互持有,形成保留环,无法释放,导致循环引用内存泄露

例子一:
self.b.test1Block = ^(NSString *text) {
      nslog(@"%@",text);
       [self.test1_btn setTitle:text forState:UIControlStateNormal];
 };

情况:未执行dealloc 内存泄露  self->b->block->self 
解决办法:打破retain cycle
__weak typeof(self) weakSelf = self;
[weakSelf.test1_btn setTitle:text forState:UIControlStateNormal];
例子二:
LHQTest2ViewController *b = [[LHQTest2ViewController alloc]init];
    [self.navigationController pushViewController:b animated:YES];
    b.test2Block = ^(NSString *text) {
        [self.test1_btn setTitle:text forState:UIControlStateNormal];
    };

情况:执行dealloc 不泄露 b->block->self
例子三:
    b = [[LHQTest2ViewController alloc]init]; //test2为局部变量
    [self.navigationController pushViewController:test2 animated:YES];
    b.test2Block = ^(NSString *text) {
        [self.test2_btn setTitle:text forState:UIControlStateNormal];
    };
情况:未执行dealloc 内存泄露 self->b->block->self 
解决办法:与例子一一样
例子四:
    self.b.test2Block = ^(NSString *text) {
        _msg = text; // _msg未局部变量
    };
情况:未执行dealloc 内存泄露 self->b->block->self 
解决办法:打破retain cycle
__weak typeof(self) weakSelf = self;
    self.b.test2Block = ^(NSString *text) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
      // strongSelf和原来的self并没有直接关系,因为strongSelf是通过weakSelf得来的,而weakSelf又没有强引用原来的sel
        strongSelf->_msg = text;
    };
4、block中基本数据类型,数值无法更改。

对于基本数据类型,进入到block中会被当做常量处理

//如果需要在block中对num进行修改,需要加上关键字__block
 //(我们也可以用static关键字进行修饰)
__block int  num = 10; // 使进入到block块中的变量不被当做常量来使用
b.test2Block = ^(NSString *text) {
     num += 1;
    NSLog(@"num is %d",num);
 };

ending 有什么错误的地方请指正。

你可能感兴趣的:(iOS_block问题)