ios block的全方位解刨(block用什么修饰,block的循环引用,weakSelf 需要配合 strongSelf 使用,操作Block外部的变量)

Block的简介

Block 的官方定义是这样的:Block块是封装工作单元的对象,是可以在任何时间执行的代码段,其本质是可移植的匿名函数,可以作为方法和函数的参数传入,可以从方法和函数中返回。

在iOS4以后,越来越多的系统级的API在使用Block。苹果对于Block的使用主要集中在如下几个方面:


 - 完成处理–Completion Handlers
 - 通知处理–Notification Handlers
 - 错误处理–Error Handlers
 - 枚举–Enumeration
 - 动画与形变–View Animation and Transitions
 - 分类–Sorting
 - 线程管理:GCD/NSOperation

第二部分:操作Block外部的变量
Q:访问Block之外的变量?

int age=10;
void (^Block)(void) = ^{
    NSLog(@"age:%d",age);
};
Block();
age = 20;
Block();

输出值为 age:10
输出值为 age:10
原因:创建block的时候,已经把age的值存储在里面了。
注意此时觉得不能对block里面age的值修改,会报错

Q:下列代码输出值分别为多少?

auto int age = 10;
static int num = 25;
void (^Block)(void) = ^{
    NSLog(@"age:%d,num:%d",age,num);
};
age = 20;
num = 11;
Block();

输出结果为:age:10,num:11
愿意:auto变量block访问方式是值传递,static变量block访问方式是指针传递
源码证明

int age = __cself->age; // bound by copy
int *num = __cself->num; // bound by copy

NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_d2875b_mi_0, age, (*num));

int age = 10;
static int num = 25;

block = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, age, &num));

age = 20;
num = 11;

上述代码可查看 static修饰的变量,是根据指针访问的

Q:为什么block对auto和static变量捕获有差异?

auto自动变量可能会销毁的,内存可能会消失,不采用指针访问;static变量一直保存在内存中,指针访问即可

Q:修改block之外的变量

在block中假如需要修改block之外定义的变量时,那么在定义变量时必须加上__block关键字这个比较常用

  self.shadowView.alpha = 0.0f;
   __block typeof(self) bself = self;
[UIView animateWithDuration:0.2f animations:^{
    
        bself.shadowView.alpha = 1.0f;
    
    } completion:^(BOOL finished){
    
    }];

或者

改变 result的结果
__block BOOL result = NO;
[self.dataBaseQueue inDatabase:^(FMDatabase *db){
    
        result = [db executeUpdate:sql];
        
    }];

Q:block在修改NSMutableArray,需不需要添加__block?
不需要。

NSMutableArray *array = [NSMutableArray array];
    void(^block)(void) = ^{
        [array addObject:@123];
    };
    Block();

这里  对 array 只是一个使用,而不是赋值,所以不需要 _ _block 进行修饰


错误的例子
  NSMutableArray *array = nil;
    void(^block)(void) = ^{
            array = [NSMutableArray array];
    };
    Block();

这里就需要在array的声明处添加__block修饰符,不然编译器会报错

总结下,对变量进行赋值的时候,下面这些不需要__block修饰符

Q:block能否修改变量值?

auto修饰变量,block无法修改,因为block使用的时候是内部创建了变量来保存外部的变量的值,block只有修改内部自己变量的权限,无法修改外部变量的权限。
static修饰变量,block可以修改,因为block把外部static修饰变量的指针存入,block直接修改指针指向变量值,即可修改外部变量值。
全局变量值,全局变量无论哪里都可以修改,当然block内部也可以修改。

Q:__block 修饰符作用?

  • __block可以用于解决block内部无法修改auto变量值的问题
  • __block不能修饰全局变量、静态变量(static)
  • 编译器会将__block变量包装成一个对象
  • __block修改变量:age->__forwarding->age
  • __Block_byref_age_0结构体内部地址和外部变量age是同一地址

在这里插入图片描述

Q:block的属性修饰词为什么是copy?

block一旦没有进行copy操作,就不会在堆上
block在堆上,程序员就可以对block做内存管理等操作,可以控制block的生命周期
@property (copy, nonatomic) CMBCAddressUIBlock finishCompletion;

Block分类**

block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型

__NSGlobalBlock __ ( _NSConcreteGlobalBlock )
__NSStackBlock __ ( _NSConcreteStackBlock )
__NSMallocBlock __ ( _NSConcreteMallocBlock )

各类型的block在内存中如何分配的?

  • __NSGlobalBlock __ 在数据区
  • __NSMallocBlock __ 在堆区(访问外部变量强引用在堆区)
  • __NSStackBlock __ 在栈区(访问外部变量弱引用在栈区)
  • 堆:动态分配内存,需要程序员自己申请,程序员自己管理
  • 栈: 自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况
    在这里插入图片描述

对每种类型block调用copy操作后是什么结果?

  • __NSGlobalBlock __ 调用copy操作后,什么也不做
  • __NSStackBlock __ 调用copy操作后,复制效果是:从栈复制到堆;副本存储位置是堆
  • __NSStackBlock __ 调用copy操作后,复制效果是:引用计数增加;副本存储位置是堆

对象类型的auto变量**

Q:当block内部访问了对象类型的auto变量时,是否会强引用?
答案:分情况讨论,分为栈block和堆block
栈block
a) 如果block是在栈上,将不会对auto变量产生强引用
b) 栈上的block随时会被销毁,也没必要去强引用其他对象
堆block
1.如果block被拷贝到堆上:
a) 会调用block内部的copy函数
b) copy函数内部会调用_Block_object_assign函数
c) _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
2.如果block从堆上移除
a) 会调用block内部的dispose函数
b) dispose函数内部会调用_Block_object_dispose函数
c)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值