iOS的block

turboksiOS72

一:block的本质

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

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

3.    block的底层结构:



block底层结构



二:block的变量捕获(capture)

为了保证block内部可以正常访问外部的的变量,block有个变量捕获机制。

代码测试捕获记机制,使用    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 生成C++代码。

1.    局部变量的 auto变量:

        int  a =10;

        void(^ksblock)(void) = ^ {

            NSLog(@"%d",a);

        };

        a =20;

        ksblock();

生成的C++代码:

struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  inta;

  __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int_a,intflags=0) : a(_a) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

  }

};

可以看出ksblock将a变量捕获到了内部,并且是值传递的方式,所以打印出来结果就是10,并没有修改成功。

2.    局部变量的 static变量:

         static   int a =10;

        void(^ksblock)(void) = ^ {

            NSLog(@"%d",a);

        };

        a =20;

        ksblock();

生成的C++代码如下:

struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  int*a;

  __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,int*_a,intflags=0) : a(_a) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

  }

};

可以看出ksblock将a变量捕获到了内部,因为是指针传递,所有当把a修改成20的时候,block调用时候的a就是指针指向的最新值。

3.    全局变量:

inta=10;

intmain(intargc,constchar* argv[]) {

    @autoreleasepool {

        void(^ksblock)(void) = ^ {

            NSLog(@"%d",a);

        };

        a=20;

        ksblock();

    }

    return0;

}

生成的C++代码如下:

struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc,intflags=0) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

  }

};

可以看出ksblock并没有将全局变量a对象捕获到内部,只需要进行全局访问就可以了。

三:block的类型

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

block类型环境:

__NSGlobalBlock__            没有访问auto变量

__NSStackBlock__               访问了auto变量

__NSMallocBlock__             __NSStackBlock__调用了copy


举例说明,在MRC环境下

        void(^ksblock1)(void) = ^ {

        };

        NSLog(@"ksblock1    %@",[ksblock1  class]);


        inta =10;

        void(^ksblock2)(void) = ^ {

            NSLog(@"%d",a);

        };

        NSLog(@"ksblock2    %@",[ksblock2  class]);


        void(^ksblock3)(void) = [^ {

            NSLog(@"%d",a);

        }copy];

        NSLog(@"ksblock3    %@",[ksblock3  class]);

打印结果:



打印

但是在ARC环境下,ksblock2打印的却是__NSMallocBlock__,这是因为在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy。


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

1.    block作为函数返回值时

2.    将block赋值给__strong指针时

3.    block作为Cocoa API中方法名含有usingBlock的方法参数时

4.    block作为GCD API的方法参数时


MRC下block属性的建议写法:

@property (nonatomic, copy)  block;

ARC下block属性的建议写法:

@property (nonatomic, strong) block;

@property (nonatomic, copy)  block;

四:__block修饰符

1.    __block可以解决block内部无法修改auto变量值的问题

2.    __block不能修饰全局变量、静态变量(static)

3.    编译器会将__block变量包装成一个对象

        __block int a =10;

        void(^ksblock)(void) = ^ {

            NSLog(@"%d",a);

        };

        ksblock();

转成C++代码如下:

struct __main_block_impl_0 {

  struct __block_impl impl;

  struct __main_block_desc_0* Desc;

  __Block_byref_a_0 *a; // by ref

  __main_block_impl_0(void*fp,struct__main_block_desc_0 *desc, __Block_byref_a_0 *_a,intflags=0) : a(_a->__forwarding) {

    impl.isa = &_NSConcreteStackBlock;

    impl.Flags = flags;

    impl.FuncPtr = fp;

    Desc = desc;

  }

};

struct __Block_byref_a_0 {

  void*__isa;

__Block_byref_a_0 *__forwarding;

 int __flags;

 int __size;

 inta;

};

__Block_byref_a_0添加一个指向自身的指针,根据指针查找到对应的值。

五:block内存管理

1.    当block在栈上的时候,并不会对__block变量产生强引用。

2.    当block被copy到堆上的时候

    -    会调用block内部的copy函数

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

    -    _Block_object_assign函数会对__block变量形成强引用(retain)



copy

3.    当block从堆上移除的时候

    -    会调用block内部的dispose函数

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

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



移除


六:循环引用问题处理

ARC:

用__weak、__unsafe_unretained解决

用__block解决(必须要调用block)


MRC:

用__unsafe_unretained解决

用__block解决



相关文章

iOS 仿抖音打卡美好中国

iOS 仿抖音打卡美好中国

效果图这几天打卡中国挺火的,刚好有点时间就想着看看能不能模仿一下。Scrollview + CADisplayLink 设置背景UILongPressGestureRecognizer长按手...

iOS多线程

iOS多线程

多线程常见方案一、GCD的函数:GCD中有2个用来执行任务的函数queue:队列block:任务1.    用同步的方式执行任务dispatch_sync(di...

iOS短视频开发之---AVPlayer

iOS短视频开发之---AVPlayer

基于原生的AVPlayer + ScrollView实现,包含视频播放、暂停、点赞动画实现。视频是采用的本地视频、需要网络视频请自行添加添加测试。添加方式:  //本地视频 &n...

WKWebView

WKWebView

<!DOCTYPE html><head>    <title>HTML</title>    <meta...

暗黑模式适配

暗黑模式适配

先上效果图:正常暗黑一:首先确定自己的主题色比如正常模式下显示紫色、暗黑显示下显示灰色。二:在Assets中新建颜色:创建颜色进行设置:设置颜色左边是正常模式的颜色、右边是暗黑模式下三:举例使用&nb...

OC对象的本质

OC对象的本质

我们平时编写的oc代码、底层实现其实是C\C++代码OC代码    ——>    C\C++    ——>  &nbs...