iOS :block的是是非非

243 阅读4分钟

先看这一篇 juejin.cn/post/684490…

基本定义

 <#返回值类型#>(^<#blockName别名#>)(<#parameterTypes传参#>) = ^(<#parameters#>) {
        <#statements#>
    };
     
    //没有返回值,有一个传参数
    void(^testBlock)(NSString* ) = ^(NSString*name) {
        NSLog(@"%@",name);
    };
    testBlock(@"我是说");
    //有返回值,有两个传参数
    NSInteger(^numtotal)(NSInteger,NSInteger) = ^(NSInteger a,NSInteger b) {
        return a + b ;
    };
    NSLog(@" 两数之和:%ld",numtotal(1,2));

block本质上也是oc对象

 handleNum block1 = ^(NSInteger num){
//        NSLog(@"%ld",a);
    };
    NSLog(@"%@",[block1 class]);
    NSLog(@"%@",[[block1 class] superclass]);
    NSLog(@"%@",[[[block1 class] superclass] superclass]);
    NSLog(@"%@",[[[[block1 class] superclass] superclass] superclass]);

打印结果

可以看出,如果没有捕获外界值时,是globalblock类型,

捕获外部值

 //没有返回值,有一个传参数
    NSInteger age = 12;
    
    void(^testBlock)(NSString* ) = ^(NSString*name) {
        NSLog(@"%@--%ld",name,age);
    };
    
    NSLog(@"%ld",age);
    age = 433;
    NSLog(@"%ld",age);
    testBlock(@"我是说");

从结果来看,外部值的修改并没有影响到block内部值,这是因为,block在定义的时候,编译器已经将外部值拷贝到了它内部变量,这里进行了一次值拷贝,并不是在运行时去做的。而且是只读的。如果想要修改的话,需要添加一个__block关键字,告诉编译期对他进行捕获变量的引用,不要值拷贝。

   __block NSInteger age = 12;
    或者是用个static关键字,全局变量
    void(^testBlock)(NSString* ) = ^(NSString*name) {
        age = 3;
        NSLog(@"%@--%ld",name,age);
    };
    
    NSLog(@"%ld",age);
    age = 433;
    NSLog(@"%ld",age);
    testBlock(@"我是说");
    NSLog(@"%ld",age);

打印结果如下

  Person* age = [Person new];
    age.name= @"12";
    void(^testBlock)(NSString* ) = ^(NSString*name) {
        NSLog(@"%@--%@",name,age);
    };
    
    NSLog(@"%@",age);
    [age setName:@"33333"];
    NSLog(@"%@",age);
    testBlock(@"我是说");

如果是一个对象或者是NSMutablestring话,block内部会对象进行强引用(strong),然后结束的时候会释放。所以外部的修改会影响内部。

block内部访问外部的对象时,是有个指针指向此对象,这里修改age = nil,相当于修改了指针值,这是不被允许的。

  Person* age = [Person new];
    age.name= @"12";
    void(^testBlock)(NSString* ) = ^(NSString*name) {
         [age setName:@"dddd"];
        NSLog(@"%@--%@",name,age);
    };
    
    NSLog(@"%@",age);
    [age setName:@"33333"];
    NSLog(@"%@",age);
    testBlock(@"我是说");

而这样是可以的,因为它操作的是指针指向的那个对象,并没有改变指针的指向。

在实际开发中,很少像上边那么使用,更多的情况是作为一个参数传递。

typedef void(^handleNum)(NSInteger);

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.title = @"aaavc";
    self.view.backgroundColor = [UIColor lightGrayColor];
 
    handleNum ha =  ^(NSInteger num){
        NSLog(@"%ld",num);
    };
    //第二个参数是block
  [self numbOperation:@[@2,@4,@5,@6] andhanle:^(NSInteger num) {
        NSLog(@"%ld",num);
    }];
}

- (void)numbOperation:(NSArray*)array andhanle:(handleNum)block
{
    NSUInteger count = [array count];
    for (NSUInteger i = 0; i < count; i++) {
        block([array[i] integerValue]);
    }
}

使用block最常见的问题就是循环引用问题,循环引用也可能发生在delegate或NSTimer中,具体可以自行查阅。 前文讲过如果block内部访问了外部变量会进行值捕获,block同样是一个对象,他也有isa指针,也有引用计数,如过一个对象持有了一个block而block内部也捕获了这个对象,那么就会产生循环引用。

问题一:block中访问成员变量会引起强引用,如何避免?

{
    NSString* _name;
}

 __weak AViewController* weakVC = self;
    self.nameBlock = ^{
        __strong AViewController* vc = weakVC;
        vc->_name ;
    };

备注:self.property会调用到settergetter方法 ,vc->_name是访问成员变量到唯一方法,不走setter方法。

问题二:__strong 什么时候会用?

block里使用一个对象,如果用弱指针,对象释放,如果进行add的话,会crash,为了保证block执行过程中对象不被释放,我们可以在block内部初始化一个strong指针。文章中讲的验证了都是正确的, 需要注意arc环境下,不能直接调用retaincount,需要使用桥接

    NSLog(@"after block retainCount:%zd", CFGetRetainCount((__bridge CFTypeRef)obj));

www.jianshu.com/p/fe772a353…

问题三:block为啥用copy修饰?

block分为三种: 一 NSGlobalBlock:全局的静态block 没有访问外部变量 你的block类型就是这种类型(也就是说你的block没有调用其他外部变量) 二 NSStackBlock:保存在栈中的block,没有用copy去修饰并且访问了外部变量,你的block类型就是这种类型,会在函数调用结束被销毁 (需要在MRC) 三 NSMallocBlock 保存在堆中的block 此类型blcok是用copy修饰出来的block 它会随着对象的销毁而销毁,只要对象不销毁,我们就可以调用的到在堆中的block。

默认情况下,block会存档在栈中(栈是吃了吐),所以block会在函数调用结束被销毁,在调用会报空指针异常,如果用copy修饰的话,可以使其保存在堆区(堆是吃了拉) ,它的生命周期会随着对象的销毁而结束的。只要对象不销毁,我们就可以调用在堆中的block。

问题三:block不能修改对象的指针,为什么? 这个原因和int a类似,先看这张图

在block内部修改p,肯定改不了外边p的指向,所以报错。

block的内存结果是怎么样的?

答案: www.cnblogs.com/lybSkill/p/…

推荐阅读: www.jianshu.com/p/4e79e9a0d…