ios-Block

作者: 忽然之间_1988丶 | 来源:发表于2017-08-08 15:55 被阅读0次

    概述:

    • 能够截取自动变量的匿名函数
    • 指向函数的指针
    • 结构体
    • oc对象

    使用:

    - 声明
    格式:返回值类型(^变量名称)(参数列表)
    int(^myBlock)(int a,int b)
    void(^myBlock)(void)
    
    - 定义(变量赋值)
    格式:变量 = ^(参数列表){函数体};
    myBlock = ^(int a, int b){
         return 1;
    };
    myBlock = ^(void){
    
    };
    
    - 调用
    格式:变量(参数列表);
    myBlock(1,2);
    myBlock();
    
    - 使用typedef定义Block类型
    格式:typedef 返回值类型(^变量名称)(参数列表)
    typedef void(^MyBlock)();
    MyBlock myBlock = ^(){
    
    };
    
    - Block作为函数参数
    int(^MyBlock)(int a, int b) = ^(int a, int b){ // 声明定义一个参数为a,b,返回值为int类型的block
            return 3;
    };
    [self blockTest:MyBlock]; 
        
    void(^MyBlcok)(void) = ^(){
        
    };
    [self blockTest1:MyBlcok];
    
    - (void)blockTest:(int(^)(int a, int b))myBlock{
    }
    - (void)blockTest1:(void(^)())myBlock{
    }
    

    注意:为了简化block的声明,一般使用typedef声明block类型

    - Block作为函数返回值
    int(^MyBlock)(int a, int b) = [self blockTest];
    
    - (int(^)(int a, int b))blockTest{
        return ^(int a,int b){
            return a+b;
        };
    }
    

    block类型:

    • 全局区:NSConcreteGlobalBlock
    • 栈区:NSConcreteStackBlock
    • 堆区:NSConcreteMallocBlock
    - NSConcreteGlobalBlock
    block定义在函数之外(和全局变量一个地方)
    void (^globalBlock)() = ^{
        NSLog(@"大家好我是NSConcreteGlobalBlock-->");
    };
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            globalBlock();
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    转换成c++代码:
    struct __globalBlock_block_impl_0 {
      struct __block_impl impl;
      struct __globalBlock_block_desc_0* Desc;
      __globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
        impl.isa = &_NSConcreteGlobalBlock;  <--- 看这,这里。。
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    

    注意:在block没有截取任何自动变量的时候,也是NSConcreteGlobalBlock的,但我在ARC环境下,查看c++源码,是NSConcreteStackBlock,具体原因请看唐巧大神的说明,自行验证。

    - NSConcreteStackBlock
    定义在函数内部的block
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            void(^StackBlock)() = ^(){
                NSLog(@"大家好,我是StackBlock");
            };
            StackBlock();
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    转换成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, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    注意:NSConcreteMallocBlock无法直接创建,当执行了以下操作时,系统会自动从NSConcreteStackBlock copy到NSConcreteMallocBlock中:

    1.调用Block的copy实例方法时
    2.Block作为函数返回值返回时
    3.将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
    4.将方法名中含有usingBlock的Cocoa框架方法或GCD的API中传递Block时

    截取自动变量:

    - 局部变量
    • 局部变量在block中不可修改。
    • 局部变量是值传递,所以在block外面修改不影响block里截取的变量值。
    oc代码:
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            int i = 0;
            void(^MyBlock)() = ^(){
                i++; // 报错
                NSLog(@"大家好,我是MyBlock-->%d",i); // 注释掉上面的报错代码,输出的还是0;
            };
            i++;
            MyBlock();
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    c++代码:
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int i;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int i = __cself->i; // bound by copy
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_658820_mi_1,i);
    }
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            int i = 0;
            void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));
    
            ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    
    - 静态局部变量
    • 局部静态变量在block中可修改。
    • 局部静态变量是地址传递,所以在block外面修改会影响block里截取的变量值。
    oc代码:
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            static int i = 0;
            void(^MyBlock)() = ^(){
                i++; // 可以修改,不会报错
                NSLog(@"大家好,我是MyBlock-->%d",i);
            };
            i++;
            MyBlock();
    
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    c++代码:
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      int *i;
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_i, int flags=0) : i(_i) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int *i = __cself->i; // bound by copy
    
                (*i)++;
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e314e4_mi_0,(*i));
            }
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            static int i = 0;
            void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &i));
    
            i++;
            ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    - __block修饰局部变量(只能用于修饰普通局部变量,static,全局都不行)
    • __block修饰的局部变量在block中可修改。
    • __block修饰的局部变量,注意看下面c++源码,系统会把用__block修饰的变量包装成一个__Block_byref_i_0结构体对象。main_block_impl_0 中引用的是 Block_byref_i_0 的结构体指针,这样就可以达到修改外部变量的作用,因为传递的是该结构体的地址,所以我们在外面修改变量,会影响block中的值。
    oc代码:
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            __block int i = 0;
            void(^MyBlock)() = ^(){
                i++; // 可以修改,不会报错
                NSLog(@"大家好,我是MyBlock-->%d",i);
            };
            
            i++;
            MyBlock();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    c++代码:
    struct __Block_byref_i_0 {  // 当用__block来修饰变量时,系统会吧变量包装成一个结构体对象。
      void *__isa;
    __Block_byref_i_0 *__forwarding;
     int __flags;
     int __size;
     int i;
    };
    
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      __Block_byref_i_0 *i; // by ref
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_i_0 *i = __cself->i; // bound by ref
    
                (i->__forwarding->i)++;
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_be9fd2_mi_0,(i->__forwarding->i));
     }
    
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->i, 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};
    
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 0};
            void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));
    
            (i.__forwarding->i)++;
            ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    
    - 全局变量
    • 全局变量在block中可修改。
    • 全局变量存储在静态数据区,在程序销毁前不会销毁,所以在block中可以直接访问,因为访问的是一份地址,所以在外面修改会影响block里面变量的值。
    oc代码:
    int i = 0;
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            void(^MyBlock)() = ^(){
                i++; // 可以修改,不会报错
                NSLog(@"大家好,我是MyBlock-->%d",i);
            };
            
            i++;
            MyBlock();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    c++代码:
    int i = 0;
    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, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
                i++;
                NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_ba21a2_mi_0,i);
    }
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
            i++;
            ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    
    - 静态全局变量
    • 静态全局变量在block中可修改。
    • 静态全局变量存储在静态数据区,在程序销毁前不会销毁,所以在block中可以直接访问,因为访问的是一份地址,所以在外面修改会影响block里面变量的值。
    oc代码:
    static int i = 0;
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            void(^MyBlock)() = ^(){
                i++; // 可以修改,不会报错
                NSLog(@"大家好,我是MyBlock-->%d",i);
            };
            
            i++;
            MyBlock();
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    c++代码:
    static int i = 0;
    
    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, int flags=0) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
           i++;
           NSLog((NSString *)&__NSConstantStringImpl__var_folders_k__jbgrnnkx4ds0dh6rz0rglp540000gn_T_main_e20576_mi_0,i);
     }
    
    static struct __main_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    
    int main(int argc, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            void(*MyBlock)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
            i++;
            ((void (*)(__block_impl *))((__block_impl *)MyBlock)->FuncPtr)((__block_impl *)MyBlock);
    
            return UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
        }
    }
    
    

    内存管理:

    - (void)viewDidLoad {
        
        [super viewDidLoad];
        
        self.view.backgroundColor = [UIColor whiteColor];
        
        int i = 0;
        MyBlock = ^(){
            NSLog(@"我是MyBlock--->%d",i);
        };
        MyBlock();
        
        NSLog(@"myBlock--->%@",MyBlock);
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        MyBlock();
    }
    
    
    • MRC
      引用了局部变量i,所以block在NSStackBlock中,出了viewDidLoad方法,MyBlock会自动销毁,touchesBegan再调用MyBlock就会报野指针错误。
      需要手动调用Block_copy方法把block复制到NSConcreteMallocBlock中,记得调用Block_release方法使计数器-1。
    • ARC
      会自动调用copy方法,把block复制到NSConcreteMallocBlock中。

    注意:在Block中无论是MRC/ARC,没有调用局部变量时,都是NSConcreteGlobalBlock类型的,也就不会出现block销毁了,在调用的情况了。

    image.png

    循环引用

    • 调用系统的block不会循环引用,self并不持有该方法。
    [UIView animateWithDuration:0.5 animations:^{
            NSLog(@"%@", self);
    }];
    
    • 当self持有block时,block中又调用了self,会循环引用。
    @property (nonatomic, copy)MyBlock myBlock;
    self.myBlock = ^(){
       self.xxxx;
    };
    
    // 不会循环引用
    __weak typeof(self)weakSelf = self;
    self.myBlock = ^(){
      weakSelf.xxxx;
    };
    
    • 自定义对象中有个block属性,在调用该对象的block属性中,调用该对象的其他属性,会循环引用。
    typedef void(^MyBlock)();
    
    @interface MyBlockModel : NSObject
    @property (nonatomic, copy) MyBlock myBlock;
    @property (nonatomic, copy) NSString *text;
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        
        MyBlockModel *model = [[MyBlockModel alloc] init];
        model.myBlock = ^(){
            NSLog(@"--->%@",model.text);
        };
    
       // 不会循环引用
        MyBlockModel *model1 = [[MyBlockModel alloc] init];
        __weak MyBlockModel *weakModel = model1;
        model1.myBlock = ^(){
            NSLog(@"--->%@",weakModel.text);
        };
    }
    
    • 注意:在异步(多线程)环境下。防止self被释放,应该在block内部用强引用引用该弱引用。
    __weak typeof(self)weakSelf = self;
    self.myBlock = ^(){
        __strong typeof (weakSelf)strongSelf = weakSelf;
        if(strongSelf){
            strongSelf.xxxx; 
        }
    };
    

    参考:
    http://www.jianshu.com/p/51d04b7639f1
    http://www.cocoachina.com/ios/20161025/17198.html
    http://blog.devtang.com/2013/07/28/a-look-inside-blocks/

    小提示:
    1.如和把oc代码转成c++代码:
    通过终端cd到你要编译的文件所在目录,输入:clang -rewrite-objc xxxx.m即可。(xxxx:你要编译的文件名称)。
    2.如果出现main.m:9:9: fatal error: 'UIKit/UIKit.h' file not found怎么办?
    clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode8.3.3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk xxxx.m(xxxx:你要编译的文件名称)。

    相关文章

      网友评论

          本文标题:ios-Block

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