Block

作者: bytebytebyte | 来源:发表于2021-04-14 14:48 被阅读0次
1.block本质是封装了函数调用以及函数调用环境的oc对象。自己要会根据源码分析出这个结论记忆会更深刻。
函数调用:函数地址
函数调用环境:block 访问外部的变量
OC对象:有isa指针

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
};

struct __block_impl {
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};

struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    int age;
};
        int age = 20;
        void (^block)(int, int) =  ^(int a , int b){
            NSLog(@"this is a block! -- %d", age);
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
            NSLog(@"this is a block!");
        };
        struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
        block(10, 10);

2.请分析为什么打印的值是10?
auto int age = 10; //定义出来的局部变量都是auto,可以省略不写,意思是离开所在的大括号会自动销毁
 block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d", age);
  };
age = 20;
block();

因为block具备捕获的能力,在block内已经把10这个值传递给block内部的成员变量了,所以打印10,而这个时候去修改age的值其实是修改的block外部的age。
捕获:block内部会新增一个成员变量正好是外部的变量

3.为什么自动变量是值传递不是指针传递?
因为自动变量在函数执行完后会销毁,销毁后再去访问就是垃圾数据了.
什么是自动把变量?
局部变量分2种:auto、static
auto int age = 10; //定义出来的局部变量都是auto,auto只能修饰局部变量,可以省略不写,意思是离开所在的大括号会自动销毁

void (^block)(void);
void test()
{
    auto int age = 10;
    static int height = 10;
    block = ^{
        // age的值捕获进来(capture)
        NSLog(@"age is %d, height is %d", age, height);
    };
    age = 20;
    height = 20;
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        test();
        block(); //age is 10, height is 20
      }
}

4.全局变量为什么不用捕获可以直接访问?
因为谁都可以随时访问就没有必要访问了。
为什么局部变量需要捕获?test已经结束了,block又去访问,要想访问到需要先把值存在block即捕获,即跨函数访问的原因。

5.block变量捕获的种类?3种
变量类型                                           捕获到block内部        访问方式
局部     auto 基本数据类型                          ✔️                      值传递
            auto 对象类型                                  ✔️                 连同其所有权修饰符的指针传递
变量     static                                                ✔️                     指针传递
全局    变量                                                  ×                        直接访问

究竟block会不会捕获只要搞清楚是局部变量还是全局变量就行了

6.下边例子会不会捕获?
会。只要搞清楚是局部变量还是全局变量就行了,因为OC函数默认为传递2个参数(MJPerson * self, SEL _cmd)函数的调用者和方法名。
Person.m
- (void)test {
          void(^block)(void) = ^ {
                  nslog(@"%@",self);
                   nslog(@"%@",_name);//self->name //直接将self捕获,不是单独不会_name,看_name来源,同理[self name]也一样
          }
}

7.block类型有哪几种?
         block类型                                  环境
__NSGlobalBlock__                  没有访问auto变量
__NSStackBlock__                     访问了auto变量
__NSMallocBlock__          __NSMallocBlock__调用了copy

每种block调用copy后的结果:
               block的类                            副本源的配置存储域                    复制结果
__NSConcreteGlobalBlock__                          栈                                         堆
__NSConcreteStackBlock__                         数据段                             什么也不做
__NSConcreteMallocBlock__                          堆                                  引用计数增加

8.ARC下block自动copy的情况有哪些?
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
(1)block作为函数返回值时
typedef void (^MJBlock)(void);
MJBlock myblock()
{
    int age = 10;
    return ^{
        NSLog(@"---------%d",age);
    };
}
main() {
        MJBlock block = myblock();
        block();
        NSLog(@"%@", [block class]); //__NSMallocBlock__
}

(2)将block赋值给__strong指针时
        int age = 10;
        MJBlock block = ^{
            NSLog(@"---------%d", age);
        };
        NSLog(@"%@", [block class]); //__NSMallocBlock__

(3)block作为Cocoa API中方法名含有usingBlock的方法参数时
[arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                    
        }];
(4)block作为GCD API的方法参数时
dispatch_once(&onceToken, ^{
            
        });

9.栈和堆访问对象类型的auto变量时都发生了什么?
栈block不会对auto变量产生强引用;
block被拷贝到堆上,block会调用copy函数

10.打印什么?怎么解释?-什么时候被释放的问题?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    MJPerson *p = [[MJPerson alloc] init];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1-------%@", p);
    });
    NSLog(@"touchesBegan:withEvent:");
}
/*
touchesBegan:withEvent:
1-------<MJPerson: 0x6000008f04b0>
MJPerson - dealloc
*/
block作为GCD的参数会发生copy,将栈上的block复制到堆上;
MJPerson *p是对象类型的强引用,在堆block内部会强引用p,在1s后才会释放block,在释放block前会先释放block强引用的p

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    MJPerson *p = [[MJPerson alloc] init];
__weak MJPerson *weakP = p;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2-------%@", weakP);
    });
    NSLog(@"touchesBegan:withEvent:");
}
/*
touchesBegan:withEvent:
MJPerson - dealloc
2-------(null)
*/
block作为GCD的参数会发生copy,将栈上的block复制到堆上;
在大括号结束时p 会被释放掉;
MJPerson *p是对象类型的弱引用,在堆block内部会弱引用p,在1s后才会释放block,而此时weakP已被释放所以null。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    MJPerson *p = [[MJPerson alloc] init];
    __weak MJPerson *weakP = p;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1-------%@", p);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-------%@", weakP);
        });
    });
    NSLog(@"touchesBegan:withEvent:");
}
2021-04-11 11:36:34.831422+0800 Interview03-测试[17362:9913914] touchesBegan:withEvent:
2021-04-11 11:36:34.831549+0800 Interview03-测试[17362:9913914] MJPerson - dealloc
2021-04-11 11:36:35.929587+0800 Interview03-测试[17362:9913914] 2-------(null)
2个block整体看待即可,2s后被释放。

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    MJPerson *p = [[MJPerson alloc] init];
    __weak MJPerson *weakP = p;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1-------%@", weakP);
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-------%@", p);
        });
    });
    NSLog(@"touchesBegan:withEvent:");
}
2021-04-11 13:41:36.149167+0800 Interview03-测试[17689:9981812] touchesBegan:withEvent:
2021-04-11 13:41:37.149242+0800 Interview03-测试[17689:9981812] 1-------<MJPerson: 0x6000028b4230>
2021-04-11 13:41:39.342495+0800 Interview03-测试[17689:9981812] 2-------<MJPerson: 0x6000028b4230>
2021-04-11 13:41:39.342795+0800 Interview03-测试[17689:9981812] MJPerson - dealloc
2个block整体看待即可,第2个block强引用p,3s后被释放。

总结:看强引用什么时候被释放。就是几秒后被销毁。

11.MRC和ARC下block写法是怎么样的?
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);

ARC下block属性的建议写法:以下2中写法等价
@property (strong, nonatomic) void (^block)(void); ARC下也会拷贝到堆上
@property (copy, nonatomic) void (^block)(void);

12.有哪些方式可以解决在block内部修改外部变量的值?改为__block,static修饰,全局变量
为什么不用static修饰,全局变量呢?因为static、全局变量会一直在内存中,只是希望在内存中暂时存一下。

13.理解__block变量
__block可以用于解决block内部无法修改auto变量值的问题
__block不能修饰全局变量、静态变量(static)
编译器会将__block变量包装成一个对象

__block int age = 10;
^{
        nslog(@"%d",age);
}();
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref
}

14.block访问__block基本数据类型、对象类型变量、__block对象类型的区别?
强引用;
由其修饰符决定;
block对__block变量结构体强引用,结构体对对象类型变量由其修饰符决定。

        __block int age = 10;
        NSObject *object = [[NSObject alloc] init]; //这种修饰符默认strong  
        __weak NSObject *weakObject = object;  //这种修饰符默认weak
        MJBlock block = ^{
            NSLog(@"%d", age);  //block强引用object
            NSLog(@"%p", weakObject); //block弱引用weakObject
        };
       block();

block对__block变量结构体强引用,ARC下结构体对对象类型变量由其修饰符决定,
                                                         MRC下结构体对对象类型变量不会强引用。
ARC:
(1)strong情况:
MJBlock block;
        {
           __block MJPerson *person = [[MJPerson alloc] init];
            block = ^{
                NSLog(@"%p", person);// block内部person结构体的person指针默认是 strong  
            };
        }
        block();
        //0x1060575b0
(2)weak情况:
        MJBlock block;
        {
            MJPerson *person = [[MJPerson alloc] init];
            __block __weak MJPerson *weakPerson = person;
            block = ^{
                NSLog(@"%p", weakPerson);//block内部person结构体的person指针默认是 weak
            };
        }
        block();
2021-04-11 23:14:48.230528+0800 Interview01-__block[19517:10315770] -[MJPerson dealloc]
2021-04-11 23:14:48.231074+0800 Interview01-__block[19517:10315770] 0x0

MRC:
main(){
        __block MJPerson *person = [[MJPerson alloc] init];
        MJBlock block = [^{
            NSLog(@"%p", person);
        } copy];
        [person release];
        block();
        [block release];
}
2021-04-11 23:29:27.590414+0800 Interview01-__block[19583:10326096] -[MJPerson dealloc]
2021-04-11 23:29:27.591165+0800 Interview01-__block[19583:10326096] 0x10051bae0
MRC下person被释放了block内部还能访问,说明是弱引用

总结;block对__block变量(基本数据类型、对象类型)结构体一定是强引用。

15、__block变量的内存管理
栈block不会对__block变量(基本数据类型、对象类型)强引用;
当栈block被copy到堆上时,调用block内部copy函数对__block变量(基本数据类型、对象类型)强应用,
当栈block被移除时,调用block内部dispose函数对__block变量(基本数据类型、对象类型)release.

__Block_byref_id_object_copy、__Block_byref_id_object_dispose是对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson。

(1)对象类型:__main_block_desc_0里copy()、dispose(),对对象类型内存管理
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); //copy函数在desc结构体里
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc; ///////////////////看这里///////////////////////
  MJPerson *__strong person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

(2)__block 基本类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理
struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age; /////////////////////看这里///////////////////////
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  NSObject *p;
  (struct)__Block_byref_age_0 *age; // by ref ///////////////////block对__block 基本类型强引用,C++中可以省略struct///////////////////////
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {////////////////////////////__main_block_copy_0///////////////////////////////
    _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 { //////////////copy、dispose在desc里//////////////
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

(3)__block 对象类型:__main_block_desc_0里copy()、dispose(),对__Block_byref_age_0内存管理,
__Block_byref_weakPerson_0里的__Block_byref_id_object_copy、__Block_byref_id_object_dispose对__Block_byref_weakPerson_0内的MJPerson *__weak weakPerson内存管理;

struct __main_block_desc_0 {//////////////////////////////__main_block_desc_0///////////////////////////////
    size_t reserved;
    size_t Block_size;
    void (*copy)(void);
    void (*dispose)(void);
};

struct __Block_byref_weakPerson_0 {////////__Block_byref_weakPerson_0//////////////////
  void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
 int __flags; // 4
 int __size; // 4
 void (*__Block_byref_id_object_copy)(void*, void*); // 8 ///__Block_byref_id_object_copy//////////
 void (*__Block_byref_id_object_dispose)(void*); // 8 ///__Block_byref_id_object_dispose//////////
 MJPerson *__weak weakPerson;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_weakPerson_0 *weakPerson; // by ref/////////////////////////////////
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

16.理解__forwarding指针:
栈上__block变量结构体的__forwarding 指向自己本身;
发生复制后:此时
栈上__block变量结构体的__forwarding 指向堆上__block变量结构体;
堆上__block变量结构体的__forwarding 指向自己本身;
总结:没发生复制__forwarding指向自己,发生了复制__forwarding指向堆上__block变量的结构。体,即没复制是自己,复制了就是堆区的结构体。

17.__weak和__unsafe_unretained区别?
__weak不会产生强引用,指向的对象销毁时,会自动让指针置为nil
__unsafe_unretained不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变。

18.block循环引用的解决方式有哪些?
ARC:__weak、__block 、__unsafe_unretained(不建议用)。
MRC:__block 、__unsafe_unretained(不建议用)。MRC不支持__weak的

__weak typeof(self) weakSelf = self;
__unsafe_unretained id weakSelf = self; 
self.block = ^{
      printf(@"%p",weakSelf);
}

__block  id weakSelf = self;
self.block = ^{
      printf(@"%p",weakSelf);
      weakSelf = nil;
}
self.block()
缺点:不调用self.block()会产生循环引用,进而造成内存泄漏。

19.block在修改NSMutableArray,需不需要添加__block?不需要。
NSMutableArray *array = [NSMutableArray array];
person.block = ^{
      [arr addObject:@"1"];
} ;

20.__block作用是什么?注意点?
修改局部变量的值,在MRC下block不会对__block变量结构体强引用。

21.block属性修饰词为什么是copy?使用block有哪些注意点?
copy在堆上可以控制block的生命周期,对block进行内存管理。
注意循环引用。


















相关文章

  • iOS开发之Block原理探究

    Block概述 Block本质 Block调用 Block分类 Block循环引用 Block原理探究 Block...

  • block的使用

    定义block 返回类型 (^block名称)(参数) = ^(){block内容}; 调用block block...

  • Block 02 - __block

    Block 02 - __block __block 的作用 __block 可以解决 Block 内部无法修改 ...

  • iOS面试之Block大全

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS面试之Block模块

    Block Block内容如下: 关于Block 截获变量 __block修饰符 Block的内存管理 Block...

  • iOS Block

    Block的分类 Block有三种类型:全局Block,堆区Block,栈区Block 全局Block 当Bloc...

  • iOS block 为什么官方文档建议用 copy 修饰

    一、block 的三种类型block 三种类型:全局 block,堆 block、栈 block。全局 block...

  • iOS开发block是用copy修饰还是strong

    Block分为全局Block、堆Block和栈Block1、在定义block没有引用外部变量的时候,block为全...

  • block 初探

    全局block, 栈block, 堆block

  • Block

    一、Block本质 二、 BlocK截获变量 三、__block 修饰变量 四、Block内存管理 五、Block...

网友评论

      本文标题:Block

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