iOS - 初识 block

2016-06-02  本文已影响74人  valentizx

block定义

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

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

block赋值

名字 = ^{xxx};

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名字) (参数列表)

上述代码可改为:

//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 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,不难看出,这里的&标志着这里是指针传递,既然是指针传递,修改里面的值肯定会影响到外部的那个变量。

上一篇下一篇

猜你喜欢

热点阅读