Objective-C block小解

block是什么

我开始理解它是和函数指针一样的东西,因为两者的写法比较类似。但是,查了些资料后发现它其实是一个对象,最主要原因是我们可以检测它的类型。(具体点这个链接)。而且这种对象和普通的oc对象不一样,它是在stack上创建的。

block的各种写法

中括号表示可选

变量

return_type (^blockName)(var_type) = ^[return_type](var_type varName)
{
// ...
};

和普通变量一样,你也可以将声明和赋值分开

return_type (^blockName)(var_type);
blockName = ^[return_type](var_type varName)
{
// ...
};

属性

推荐使用copy来修饰

@property (copy) return_type (^blockName) (var_type);

函数参数

官方建议使用block作为参数时,最好将它作为最后一个参数。

- (yourMethod_return_type)yourMethodName:(return_type (^)(var_type))blockName;

消息参数

[someObject doSomethingWithBlock: ^[return_type](var_type varName)
{
//...
}];

匿名block

^[return_type](var_type varName)
{
//...
};

typedef block

这个和typedef函数指针一样,之后就可以直接使用你自定义的名字,按照Apple文档的说法,当你的blok使用另一个block作为参数时十分有用,不用看到你眼花。。。

typedef return_type (^your_typedef_blockName)(var_type);

内联block

很少见的写法,也很少用到,相当于一个匿名block定义后立即调用

^return_type (var_type varName)
{
//...
}(var);

递归block

允许你的block调用自身,创建一个可以在回调和GCD调用时使用的loop。该block会被ARC管理回收(ps:不明觉历)

__block return_type (^blockName)(var_type) = [^return_type (var_type varName)
{
if (returnCondition)
{
    blockName = nil;
    return;
}

// ...
} copy];
blockName(varValue);

作为返回值

- (return_type(^)(var_type))methodName
{
// ...
}

block注意事项

作为property的时候要使用copy

因为block是在stack上创建的(就是所谓的stack对象,普通的oc对象都是在heap上创建的),这就和局部变量一样,意味着block只存在在创建它的那个作用域,一但离开那个作用域,就会被自动回收。

在MRC时代,你要返回一个block你要这样写:

- (void (^)(void))block { return [[^{ ... } copy] autorelease]; }

当你要在调用它的时候,你要手动向block对象发送一个copy信息,将它copy到heap上。

但是在ARC机制中,当系统检测到你在创建它的那个作用域之外调用block的时候,系统已经会自动帮你处理。只有在将block赋值给id对象才需要显式使用copy方法

id obj = [^{...} copy];

为什么我们要使用copy来修饰block的property,官方文档的解析是,即使在ARC下会自动帮你处理,使用copy进行标识,也是一种比较良好的风格。

补充一点:官方文档建议不要这样写,代码来自Apple文档

void dontDoThis() {
void (^blockArray[3])(void);  
for (int i = 0; i < 3; ++i) {
    blockArray[i] = ^{ printf("hello, %d\n", i); };
}
}

void dontDoThisEither() {
void (^block)(void);
int i = random():
if (i > 1000) {
    block = ^{ printf("got i at: %d\n", i); };
}
}

原因就是block对象可能被回收,使代码失效。正确的写法是调用copy方法(测试了很多次都没有失效的情况发生,个人认为ARC有可能也做了处理,这个链接也指出在ARC中已经自动做了处理,但是官方文档并没有删除这一点,所以补充一下)

void dontDoThisEither() {
void (^block)(void);
int i = random():
if (i > 1000) {
    block = [^{ printf("got i at: %d\n", i); } copy];
}
}

block引用self的时候要避免循环引用

有时候我们要在block中使用self,在这种情况下就会产生循环引用。

 self.block = ^{
    [self doSomething];                             
  }

self持有一个block对象,block代码块内又持有self。这个时候,常见做法是创建一个__week修饰的self类型的指针,将之指向self。

__weak yourClass *weakSelf = self;
self.block = ^{
    [weakSelf doSomething];                             
  }

或者另外一种更具逼格的写法:

__weak typeof(self) weakSelf = self;
    self.block = ^{
    [weakSelf doSomething];                             
  }

block代码块内可以捕捉外部变量

这一点比较常见,就是block代码块内可以直接使用外部的变量

    int x = 123;

    void (^printX)(void) = ^(void){

        NSLog(@"%d",x);
    };

很明显调用block时可以输出x的值,再看下面这种情况

    int x = 123;

    void (^printX)(void) = ^(void){

        NSLog(@"%d",x);
    };

    x = 23;

    printX();

这里输出的值是123,而不是23。默认情况下,block内部会copy一份x,即使之后对x进行了修改,block也无法检测到最新的值。同时,copy的那份x是const的,block不能修改x,所以以下代码是不正确的

int x = 123;

void (^printX)(void) = ^(void){
    x += 1;  
    NSLog(@"%d",x);
 };

如果要修改外部变量的值或者要检测到外部变量值的变化,可以将变量用__block修饰

__block int x = 123;

这样之后,第一个例子输出23,第二个例子输出124

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值