zoukankan      html  css  js  c++  java
  • Objective-C block深入理解

    一、block是什么?

    block是带有自动变量(局部变量)的匿名函数。它是C语言的扩展功能,C语言标准并不支持block。 

    block是Objective-C的闭包实现,正如C++中的Lambda表达式。闭包简单理解即函数中的函数,闭包在JavaScript中是一个很重要的概念。作为“函数中的函数”,block跟函数很类似。 

    1、block变量 VS 函数指针变量

    return_type (^block_name) (parameter list) // ^标识block,参数名可省略
    int (^blk) (int, int) = ^(int a, int b) {
        return a+b;    
    }; // 创建一个匿名block,并赋值给一个名为blk的block变量
    
    int ret = blk(10, 20); // block调用
    return_type (*fptr_name) (parameter list) // 函数指针,参数名可省略
    int f(int a, int b) {
        return a+b;    
    } // 函数定义
    int (*fptr)(int, int); // 函数指针声明
    
    fptr = f; // 函数名f其实就是个指针
    
    int ret = fptr(10, 20); // 通过函数指针调用函数

    通常的变量定义语句是像下面这样的,但是block变量的语法形式比较难记:

    变量类型 变量名 = 变量值;

    为此,可以用typedef给block变量类型起个简单的别名 

    typedef int (^blk) (int, int); // 相比正常的block变量声明,只是前面多了typedef,blk就变成了这种block变量类型的别名。
    
    blk sumBlk = ^(int a, int b) {
        return a+b;
    }; // 这样就回到熟悉的变量定义语句格式了  

    2、block也是一种对象,根据它在内存中的位置,分为全局block、栈block和堆block。可以通过NSLog打印识别具体是哪一种block对象。

    1)没引用外部变量的是全局block

    int a = 1;
    void (^blk) () = ^{
        //NSLog(@"%d", a);
    };
    NSLog(@"%@", blk); // <__NSGlobalBlock__: 0x102d23be0>

    2)引用了外部变量,在MRC下是栈block,可以通过[block copy]把栈block拷贝到堆上,转成堆block;在ARC下,很多时候系统帮我们完成了拷贝,具体什么时候,要实测。

    int a = 1;
    void (^blk) () = ^{
        NSLog(@"%d", a);
    };
    NSLog(@"%@", blk); // ARC下 <__NSMallocBlock__: 0x60400005ba50>
    // MRC下 <__NSStackBlock__: 0x7ffeefbff518>

    3)栈block,类似局部变量,出了它的作用域,会被系统回收内存,再调用可能导致程序崩溃。 

    4)堆block,正如其他Objective-C对象那样,利用引用计数进行内存管理。 

    3、在block里面,默认不能修改外部变量。这里指的是不能修改变量本身,如果是指针类型,它指向的内容是可以修改的。

    局部变量

    全局变量

    静态变量

    __block变量

    成员变量

    属性

    - (void)testBlock {
        int i = 10;
        NSLog(@"[%p] [%d]", &i, i);
        void (^blk)() = ^(){
            NSLog(@"[%p] [%d]", &i, i);
            i++; // 不能修改,报错:Variable is not assignable (missing __block type specifier)
        };
        blk();
    }
     
    输出:
    [0x60800044e810] [10]
    [0x60800044e810] [10]
    - (void)testBlock {
        NSMutableString *str = [[NSMutableString alloc] initWithString:@"abc"];
        NSLog(@"[%p] [%@]", str, str);
        void (^blk)() = ^(){
    		NSLog(@"[%p] [%@]", str, str);
    		[str appendString:@"def"]; // 可以修改指针指向的内容
    		str = [[NSMutableString alloc] initWithString:@"another str"]; // 不能修改指针本身,报错:Variable is not assignable (missing __block type specifier)
        };
        blk();
        NSLog(@"[%p] [%@]", str, str);
    }
    
    输出:
    [0x60000025df10] [abc]
    [0x60000025df10] [abc]
    [0x60000025df10] [abcdef]

      

      

      

    struct Block_descriptor {
        unsigned long int reserved;
        unsigned long int size;
        void (*copy)(void *dst, void *src);
        void (*dispose)(void *);
    };
    
    struct Block_layout {
        void *isa;
        int flags;
        int reserved; 
        void (*invoke)(void *, ...);
        struct Block_descriptor *descriptor;
        /* Imported variables. */
    };

    5、循环引用

    1)原因:对象A有block属性,即持有block;而block中又用了对象A,使得block也持有了对象A。

    self.blk = ^{
        [self method];
    };
    

    2)

    __weak typeof(self) weakSelf = self;
    self.blk = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        [self method];
    };
    

    block不会捕获形参到内部持有

    block也是对象

    自动型,托管型,变量绑定

    ========================================================

    循环引用

    成员变量

    持有对象的成员变量即间接持有对象。

    __block修饰符可以用来避免循环引用?block不会持有__block对象。

    __block修饰符在MRC和ARC下区别很大?

  • 相关阅读:
    fedora安装后的配置
    linux下的Shell编程(7)使用-x和-n调试shell程序
    linux下的Shell编程(8)自定义函数
    linux下的Shell编程(6)case和select
    linux下的Shell编程(5)循环
    linux下的Shell编程(4)特殊的变量和占位符
    linux下的Shell编程(3)shell里的流程控制
    Linux下的Shell编程(2)环境变量和局部变量
    Linux下的Shell编程(1)最简单的例子
    Docker学习笔记
  • 原文地址:https://www.cnblogs.com/yangwenhuan/p/9099612.html
Copyright © 2011-2022 走看看