美文网首页
iOS - 初识 block

iOS - 初识 block

作者: valentizx | 来源:发表于2016-06-02 21:59 被阅读74次

block定义

  • 格式:

返回类型 (^block名字) (参数列表);

  • 同C语言的定义函数指针一样,C语言的函数指针定义格式:

返回类型 (*指针名字) (参数列表);

block赋值

名字 = ^{xxx};

  • 如同普通的变量赋值一样类型、分号都需要‘一一对应’,如同int a = 5;^标志右边的代码段是block类型。

block调用

block名字 (参数列表);

例(无参):

//定义
 void (^valenti) ();
//赋值
 valenti = ^{
        NSLog(@"VaLenTi is MEEEEE!");
    };
//调用
 valenti();

例(有参):

//定义
 void (^valenti) (NSString* name);
//赋值
 valenti = ^(NSString* name){
        NSLog(@"VaLenTi is %@!",name);
    };
//调用
 valenti(@"ME");

例(有参有返回值):

//定义
 NSInteger (^sum)(NSInteger value1, NSInteger value2);
//赋值    
 sum = ^(NSInteger value1, NSInteger value2){
        return value1 + value2;
    };
//调用
 NSInteger result = sum(1,1);

typedef与block

如若有这样的需求:定义四个block实现两个参数的加减乘除,他们的代码是如下这样的:

    //加
    NSInteger (^add)(NSInteger value1, NSInteger value2);
    add = ^(NSInteger value1, NSInteger value2){
        return value1 + value2;
    };
    NSInteger result = add(1,1);
   
    //减
    NSInteger (^sub)(NSInteger value1, NSInteger value2);
    sub = ^(NSInteger value1, NSInteger value2){
        return value1 - value2;
    };
    NSInteger result2 = sub(1,1);
    
    //乘
    NSInteger (^mul)(NSInteger value1, NSInteger value2);
    mul = ^(NSInteger value1, NSInteger value2){
        return value1 * value2;
    };
    NSInteger result3 = mul(1,1);
    
    //除
    NSInteger (^div)(NSInteger value1, NSInteger value2);
    div = ^(NSInteger value1, NSInteger value2){
        return value1 / value2;
    };
    NSInteger result4 = div(1,1);

可见,除了block的名字和操作不同以外,其余的结构都是相同的,那么,相同的部分即可用typedef起别名的形式“抽取”出来。格式如下:

typedef 返回值类型 (^block名字) (参数列表)

  • 同C语言的函数指针别名一样,名称即代表别名。

上述代码可改为:

//1.在类扩展处定义别名
typedef NSInteger (^calculate) (NSInteger value1,NSInteger value2);
//2.在实现中定义对应功能的block代码并赋值
//加
calculate add = ^(NSInteger value1, NSInteger value2){
    return value1 + value2;
 };
 NSInteger result = add(1,1);
//减
calculate sub = ^(NSInteger value1, NSInteger value2){
    return value1 - value2;
};
NSInteger result2 = sub(1,1);
//乘
calculate mul = ^(NSInteger value1, NSInteger value2){
    return value1 * value2;
};
NSInteger result3 = mul(1,1);
//除
calculate div = ^(NSInteger value1, NSInteger value2){
    return value1 / value2;
};
NSInteger result4 = div(1,1);

注意事项

  1. block可以访问外部变量,例:
int a = 10;
void (^block)() = ^{
      NSLog(@"%zd",a);
}
  1. block中可以定义和外界同名的变量,在block内部外部存在同名变量的情况下,block访问的变量是内部变量-“就近原则”。

  2. 默认情况下,不可以在block内部修改外部的变量,1中block内部是不可以对a进行赋值的,因为block中的a和外部的a本质上并不是同一个a,block访问的外部变量会将外部的变量拷贝一份到堆内存中,验证:

int a = 5;
NSLog(@"%p",&a);
void (^block)()= ^{
      NSLog(@"%p",&a);
};
block();

结果是:

0x7fff53f42a2c
0x7fb601475220

  • 如果想在block中修改外界变量的值,必须在外界变量前面加上__block,在内部修改了变量的值会直接影响外部的值,但是内部外部的变量依然不是同一个,他们的内存地址依然不同
__block int a = 5;
NSLog(@"%p",&a);
void (^block)() = ^{
    a = 10;
    NSLog(@"%p",&a);
};
block();

结果是:

0x7fff5100ea88
0x7fe931629858

那么,加上__block就可以的本质原因就是传值方式的原因。

  1. 把未加__ block修饰的那段代码的ViewController.m的代码编译成C++代码,如图:
未加__block修饰的.cpp代码文件

画框代码即是核心部分,如下:


int a = 5;
    void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, a));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

历尽艰难险阻终于找到a的影子,其中__ViewController__viewDidLoad_block_impl_0是一个结构体,在这里传了三个参数:(void *)__ViewController__viewDidLoad_block_func_0&__ViewController__viewDidLoad_block_desc_0_DATAa,不难看出,这里是直接将a作为参数传递,也就是值传递,既然是值传递,修改里面的值对外部的a自然是无效的。

那么用__block修饰代码的cpp文件,就如下图:

用__block修饰的.cpp代码文件

画框部分是核心代码,如下:

 __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 5};
    void (*block)() = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

仅仅加了一个修饰词,代码就有了不小的变动,这里的结构体传了四个参数,a在第三个位置,并且a也有了修饰:(__Block_byref_a_0 *)&a,不难看出,这里的&标志着这里是指针传递,既然是指针传递,修改里面的值肯定会影响到外部的那个变量。

相关文章

  • iOS底层探索之Block(二)——如何解决Block循环引用问

    Block你知道几种?Block的循环引用你有几种解决办法呢? iOS底层探索之Block(一)——初识Block...

  • iOS - 初识 block

    block定义 格式: 返回类型 (^block名字) (参数列表); 同C语言的定义函数指针一样,C语言的函数指...

  • 块与GCD

    没读这本书之前,我对 block理解的成长过程; block编程对于许多初识iOS编程的人来说,很难理解和运用,我...

  • iOS-2 Block

    block块 系列文章: iOS Block浅浅析 - 简书 iOS Block实现原理 iOS Block __...

  • iOS Block存储域及循环引用

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block __block说明符...

  • iOS Block实现原理

    系列文章:iOS Block概念、语法及基本使用iOS Block __block说明符iOS Block存储域及...

  • iOS Block __block说明符

    系列文章:iOS Block概念、语法及基本使用iOS Block实现原理iOS Block存储域及循环引用 上一...

  • iOS复习之Block

    iOS面试中如何优雅回答Block iOS block循环引用

  • block系列文章总结

    iOS源码解析:Block的本质<一>iOS源码解析:Block的本质<二>Objective C block背后...

  • block学习

    block学习 根据网上的block各种大神的资料,加入自己的理解。 一、初识block 1. block作用 b...

网友评论

      本文标题:iOS - 初识 block

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