block

1.block的原理是什么?本质是什么?

block本质上也是一个OC对象,它内部也有一个isa指针,

block是封装了函数调用以及函数调用环境的OC对象

block的底层结构如上


block转成C++代码之后的结构

2.block的类型

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

最终继承自NSObject


block类型
每一种类型的block调用copy后的结果
应用程序内存分配

3.block的属性修饰词为什么是copy?使用block有哪些使用注意?

block没有copy是不会在堆上的,

使用注意:循环引用问题

另外

在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况

-block作为函数返回值时

-将block赋值给__strong指针时

-block最为Cocoa API中方法名含有usingBlock的方法参数时

-block作为GCD API的方法参数时

--------------------------

MRC下block属性的建议写法

@property (copy,nonatomic) void (^block)(void);

ARC下block属性的建议写法

@property (copy,nonatomic) void (^block)(void);

@property (strong,nonatomic) void (^block)(void);

MRC下手动调用copy也会到达堆区,MRC下赋值不会触发copy操作,所以MRC下不手动调用个copy就是_NSConcreteStackBlock

4.block在修饰NSMutableArray,需不需要添加__block?

不需要,因为block会把指针捕获到block中,NSMutableArray相应的操作只是使用指针,并没有改变指针,但是如果要把数组置为nil则会编辑报错 Variable is not assignable (missing __block type specifier)(变量不可赋值)

5.为了能够保证block内部能够正常访问外部的变量,block有个变量捕获机制

捕获的内层机理


变量的捕获


6.block改变外部变量值的两种方式?

1.传递内存地址到block中(捕获变量)

2.改变存储区的方式(__block)

将auto从栈copy到堆,

原因:栈中内存管理是有系统管理,出了作用域就会被清除,堆中才是可以由程序员管理,

7.block不允许修改外部变量的值?

原因:->

-block本质上是一个对象,block的花括号区域是对象内部的一个函数,变量进入花括号,实际就是已经进入另一个函数区域---改变了作用域

-在几个作用域之间进行切换呢时,如果不加上这样的限制,变量的可维护性将大大降低

-比如想在block内部声明了一个与外部同名的变量,此时是允许还是不允许呢?只有加上了这样的限制,这样的场景才能实现

-所以Apple在编译器层面做了限制,如果block内部试图修改auto变量(无修饰符),那么直接编译报错

-可以把编译器的这种行为理解为:对block内部捕获到的auto变量设置为只读属性----不允许直接修改

8.如何实现对外部变量的捕获?

-将变量设置为全局变量.原理:block内外可直接访问全局变量

-加static(放在静态存储区/全局初始化区),原理是block内部都外部的auto变量进行指针捕获

最优解:使用 __block关键词修饰

9.__block修饰符

-__block可以用于解决block内部无法修改auto变量值的问题

-__block不能修饰全局变量,静态变量(static)(__block attribute not allowed, only allowed on local variables)

-将auto变量封装为结构体(对象),在结构体内部新建一个同名的auto变量

-block内部截获该结构体的指针

-在block中使用自动变量时,使用指针指向的结构体中的自动变量

        __block int var = 10;

        void(^blk)(void) = ^{

            var = 20;

        };

        blk();

        转换后的代码:

        struct __Block_byref_var_0 {

            void *__isa;

            __Block_byref_var_0 *__forwarding;

            int __flags;

            int __size;

            int var; // 10 => 20 该结构体持有相当于原来自动变量的成员变量

        };

        struct __main_block_impl_0 {

            struct __block_impl impl;

            struct __main_block_desc_0* Desc;

            __Block_byref_var_0 *var; // by ref

            __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) {

                impl.isa = &_NSConcreteStackBlock;

                impl.Flags = flags;

                impl.FuncPtr = fp;

                Desc = desc;

            }

        };

forwarding指针指向自己


10.block在修饰NSMutableArray需不需要添加__block

-不需要

-当变量时一个指针的时候,block里只是复制了一份这个指针,两个指针指向同一个地址

-所以,在block里面对指针呢指向内容做的修改,在block外面也一样有效

11.block是如何捕获局部变量的?

-block捕获外界变量个时,在内部会自动给生成一个属性来保存

12.UIView动画中block回调里self要不要弱引用?

-不需要,它不会造成过循环引用,因为它是类方法

-之所以需要弱引用本身,是因为怕对象之间产生循环引用,当前控制器不可能强引用一个类,所以循环无法形成

13.block里面会不会存在self为空的情况(weak strong的原理)?

        __weak typeof(self) weakself = self;
        [self wj_refresh_addRefreshHeader:^{
            __strong typeof(weakself) strongself = weakself;
            [strongself.dataSource reloadDataWithCompletion:nil];
        }];

-有时候weakSelf在block里执行reloadDataWithCompletion的时候还存在

-但在执行reloadDataWithCompletion前,可能会被释放掉

-为了保证self在block执行过程中一直存在,对他强引用strongself

14.__block与__weak的区别?

-__block不管时ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型

-__weak只能在ARC模式下使用,也只能修饰对象(NSStringg),不能修饰基本数据类型(int)

-__block对象可以在block中被重新赋值,__weak不可以

15.多层block嵌套如何使用weakSelf?

        __weak typeof(self) weakself = self;
        [self wj_refresh_addRefreshHeader:^{
            __strong typeof(weakself) strongself = weakself;
            __weak typeof(self) weakSelf2 = strongself;
            [strongself.dataSource reloadDataWithCompletion:^(BOOL result) {
                __strong typeof(self) strongSelf2 = weakSelf2;
            }];
        }];

16.Masonry对于block内部引用self会不会造成循环引用?

-不会

-这个block没有copy,是在栈上,使用完直接释放了.

        - (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block {
            self.translatesAutoresizingMaskIntoConstraints = NO;
            MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
            block(constraintMaker);
            return [constraintMaker install];
        }

17.解决block的循环引用的方式

第一种

__weak typeof(self) weakSelf = self;
self.block = ^(NSString *name) {
    __strong typeof(self) strongSelf = weakSelf;
    strongSelf.name = name;
};
self.block(@"name1");

第二种(注意,如果不执行block也会导致内存泄漏)

__block Person *p = self;

self.block = ^(NSString *name) {

       p.name = name;

      p = nil; //最后释放掉p变量 

};

self.block(@"name1");

第三种

self.block = ^(Person *p, NSString *name) {

     p.name = name;

};

self.block(self, @"name1");

18.对象类型的auto变量以及__block的内存管理

当block内部访问了对象类型的auto变量时

如果block是在栈上个,将不会对auto变量产生强引用(因为block会随时被释放)

如果block被拷贝到堆上

-会调用block内部的copy函数

-copy函数您内部会调用_Block_object_assign函数

-_Block_object_assign函数会根据auto变量的修饰符(__strong,__weak,__unsafe_unretaimed)做出相应的操作,形成强引用(retain)或者弱引用

如果block从堆上移除

-会调用block内部的dispose函数

-dispose函数您内部会调用_Block_object_dispose函数

-_Block_object_dispose函数会自动释放引用的auto变量(release)


block内的copy和dispose的调用时机

-----------未完待续------------------

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容