Stay weird. Stay different.

0%

Block备忘

Block平常编程中经常用,对于Block语法感觉很怪异,有一些细节总是想不起来,今天正好看到了别人写点Block用法很巧妙,特意来总结一下。

  1. Block语法基础

  2. Block的三种类型

  3. Block使用技巧

##Block语法基础

  1. Block单独使用:

    • Block的声明:

      1
      NSString *(^intToString)(NSUInteger para);
    • Block的定义:

      1
      2
      3
      intToString = ^NSString *(NSUInteger para){
      return [NSString stringWithFormat:@"%d",para];
      };
    • Block的调用:

      1
      intToString(1);
  2. Block作为参数:

    1
    string = [self convertIntToString:2 usingBlockObject:intToString];
  3. Block内联:

    1
    2
    3
    4
    string = [self convertIntToString:3 usingBlockObject:^NSString *(NSUInteger para) {
    return [NSString stringWithFormat:@"%d",para];
    return result;
    }];
  4. 修改Block之外的变量
    Block中调用外部变量是对变量的拷贝,修改的并不是变量本身,而是他的替身。基本类型的Block变量等效于全局变量、或静态变量,要想修改变量本身需要使用__block修饰。

    1
    2
    3
    4
    __block int a = 0;
    void (^foo)(void) = ^{
    a = 1;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    __block int base = 100;
    base += 100;
    BlkSum sum = ^ long (int a, int b) {
    base += 10;
    return base + a + b;
    };
    base++;
    printf("%ld\n",sum(1,2));//输出214,计算顺序是先base++,再进入Block体中base+=10,base+a+b
    printf("%d\n",base);//输出211
  5. Block的copy:
    Block被另一个Block使用时,另一个Block被copy到堆上时,被使用的Block也会被copy。但作为参数的Block是不会发生copy的。

    1
    typedef long (^BlkSum)(int, int);
    1
    2
    3
    4
    5
    6
    7
    8
    void foo() {
    int base = 100;
    BlkSum blk = ^ long (int a, int b) {
    return base + a + b;
    };
    NSLog(@"%@", blk); // <__NSStackBlock__: 0xbff34da0>
    bar(blk);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void bar(BlkSum sum_blk) {
    NSLog(@"%@",sum_blk); // <__NSStackBlock__: 0xbff34da0>,并不会发生copy

    void (^blk) (BlkSum) = ^ (BlkSum sum) {
    NSLog(@"%@",sum); // <__NSStackBlock__: 0xbff34da0>,无论blk在堆上还是栈上,作为参数的Block不会发生copy。
    NSLog(@"%@",sum_blk); // <__NSStackBlock__: 0xbff34da0>,当blk在堆上时,sum_blk也会被copy到堆上
    };
    blk(sum_blk); // blk在栈上 <__NSStackBlock__: 0xbff34d60>

    blk = [[blk copy] autorelease]; // <__NSMallocBlock__: 0x7880fa50> 被copy到了堆上
    blk(sum_blk); // blk在堆上 blk:<__NSMallocBlock__: 0x7880fa50> sum_blk:<__NSStackBlock__: 0xbff34da0>
    }
  6. Block作为属性:
    Block作为属性要用copy属性,将Block从栈内存复制到堆内存
    同时要注意循环引用的情况,如下面代码就会产生循环引用

    1
    2
    3
    self.myblock = ^{
    [self doSomething];
    };

    解决方法是对selfweak处理

    1
    __weak typeof(self) weakSelf = self;

##Block的三种类型

  1. NSGlobalBlock:类似函数,位于text段
  2. NSStackBlock:位于栈内存,函数返回后Block将无效
  3. NSMallocBlock:位于堆内存。

    NSGlobalBlock,我们只要实现一个没有对周围变量没有引用的Block,就会显示为是它。而如果其中加入了对定义环境变量的引用,就是NSStackBlock。那么NSMallocBlock又是哪来的呢?malloc一词其实大家都熟悉,就是在堆上分配动态内存时。如果你对一个NSStackBlock对象使用了Block_copy()或者发送了copy消息,就会得到NSMallocBlock。这一段中的几项结论可从代码实验得出。

    1
    2
    3
    4
    //NSGlobalBlock:
    float (^sum)(float, float) = ^(float a, float b){
    return a + b;
    };
    1
    2
    3
    4
    5
    //NSStackBlock:
    NSArray *testArr = @[@"1", @"2"];
    void (^TestBlock)(void) = ^{
    NSLog(@"testArr :%@", testArr);
    };
    1
    2
    //NSMallocBlock:
    //NSStackBlock做copy操作,这合Block属性的copy属性是吻合的

##Block使用技巧

  1. tableView中加载数据后的界面刷新:

    1
    2
    3
    [self buildTestDataThen:^{
    [self.tableView reloadData];
    }];
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - (void)buildTestDataThen:(void (^)(void))then {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // 获取数组

    // 获取完成后回调
    dispatch_async(dispatch_get_main_queue(), ^{
    !then ?: then();
    });
    });
    }
  2. 线程锁:

    1
    2
    3
    4
    5
    6
    7
    //声明
    static void aspect_performLocked(dispatch_block_t block) {
    static OSSpinLock aspect_lock = OS_SPINLOCK_INIT;
    OSSpinLockLock(&aspect_lock);
    block();
    OSSpinLockUnlock(&aspect_lock);
    }
    1
    2
    3
    4
    //使用
    aspect_performLocked(^{
    //异步操作
    });

未完待续