美文网首页
iOS_block问题

iOS_block问题

作者: wahkim | 来源:发表于2018-07-04 10:34 被阅读28次

    前言:

    最近遇到了一个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问题

          本文链接:https://www.haomeiwen.com/subject/tuwsuftx.html