iOS的block

turboksiOS187

一: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图片圆角设置

    UIImageView * vv = [[UIImageView alloc] initWithFrame:CGRectMake(50, 500, 250, 25...

CPU和GPU

CPU和GPU

    在屏幕成像的过程中,CPU和GPU起着至关重要的作用:CPU(Central Processing Unit,中央处理器)对象的创建和销毁、对象属性的调...

iOS 之导航渐变,并且跳转返回保持滑动状态

iOS 之导航渐变,并且跳转返回保持滑动状态

导航刚开始进入为透明,根据我们的滑动确定要显示的背景色。先看一下效果图:一:创建demo,将3个界面添加到控制器中,此过程比较简单代码就不上了。二:在第一页的viewDidload中将当前页面的导航设...

GCD定时器

GCD定时器

NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时。而GCD的定时器会更加准时。- (void)viewDidLoad {   ...

iOS程序的内存布局

iOS程序的内存布局

    代码段:编译之后的代码    数据段:字符串常量:比如NSString * str = @“123”已初始化数据:已初始化的全局变量...

Runtime详解

Runtime详解

The Objective-C language defers as many decisions as it can from compile time and link time to runti...