谈谈Objective-C中block块

在Objective-C中block可以当做匿名函数,其本质和只读属性的变量很相似,使用block,就可以像其他标准函数一样,传入参数,并得到返回值,也可以传递回调函数。

标准格式:

a(^b)(c)=^(d){
   
};
a: 返回值类型,可以是对象或者基本类型,也可以是无返回值的void
^: 块的语法标记,声明b为一个block对象
b: block对象名称
c: 实参列表,可以是对象或者基本类型,也可以是block对象,也可以是void
d: 形参列表,和实参列表对应

例如:

 int (^exmpleBlock)(int a, int b)=^(int a ,int b){
     return  a + b;
 };
int result = exmpleBlock(1,2);

block的常见类型

1.无返回值、无参数的block

void (^exmpleBlock)()=^(){
    NSLog(@"exmpleBlock");
};
exmpleBlock();

定义block的括号可以省去,如

void (^exmpleBlock)()=^{
    NSlog(@"exmpleBlock");
 };
exmpleBlock();

使用 dispatch_block_t 定义的block是一个无返回值无参数的Block

dispatch_block_t exmpleBlock = ^{
    NSlog(@"exmpleBlock");
};
exmpleBlock();
  1. 无返回值、有参数的block
void (^exmpleBlock)(int a,int b)=^(int a,int b){
    NSLog(@"sum: %d",a+b);
};
exmpleBlock(1,2);
  1. 有返回值、有参数的block
int (^exmpleBlock)(int a,int b)=^(int a,int b){
     return  a +b;
 };
 int  result =  exmpleBlock(1,2);

4.有返回值、无参数的block。形式上可以存在,但基本不会这样用,就相当于直接赋值,何必多次一举呢

 int (^exmpleBlock)()=^{
      return 1;
  };
 int  result =  exmpleBlock();

block内部不能修改外部的变量,除非使用__block修饰,如:

 __block int sum = 0;
 void (^exmpleBlock)(int a, int b)=^(int a, int b){
      sum = a+ b;
  };
  exmpleBlock(1,2);
  NSLog(@"sum: %d”,sum);

前文说到block可以看作一个对象,也可以作为对象的一个属性。

例子1 :
在对象Person中定义Block属性:

typedef void(^exmpleBlock)(NSString *msg);

@interface Person : NSObject

@property (nonatomic, copy) exmpleBlock block;

也可以这样

@property (nonatomic, copy) void(^exmpleBlock)(NSString *msg);
初始化:
Person *person = [[Person alloc] init];
person.block = ^(NSString *ms){     
  NSLog(@“%@”,msg);
}

使用:
Person.block(@“Test”);

例子2:

- (void)exmple:(int)a Block1:(void(^)(NSString *msg))block1 Block2:(void(^)())block2{
    if (a == 1) {
        block1(@"1");
    }else{
        block2();
    }
    
}

[self exmple:1 Block1:^(NSString *msg) {
        NSLog(@"传递的参数是%@",msg);
    } Block2:^{
        NSLog(@"传递的参数是2");
  }];

循环引用

对象A持有对象B,对象B持有对象A,相互持有,这样两者的retainCount值一直都无法为0,于是内存始终无法释放,导致内存泄露。

block中循环引用问题

由于block会对block中的对象进行持有操作,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,则会造成循环引用。

例如:

@interface Person  ()
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void (^exmpleBlock)();
@end

Person *person = [[Person alloc] init];
person.name = @"joe";
   person.exmpleBlock = ^{
   NSLog(@"%@",person.name);
 };

此时必然提出警告:Capturing ’person’ strongly in this block is likely to lead to a retain cycle

只要block中用到了对象的属性或者函数方法,block就会持有该对象,如例子中person强引用了block,block在回调时又强引用了person。

解决方案:

  __weak Person *weakPerson = person;
//也可以这么写  __weak typeof(person) weakPerson = person;
 person.exmpleBlock = ^{
     NSLog(@"%@",weakPerson.name);
 };

weak引用的对象如果被释放了,那么对应的weak对象就会被指为nil

当然不要一看到block就考虑用__weak 修饰。

当 block 本身不被 self 持有,而被别的对象持有,同时不产生循环引用的时候,就不需要使用 weak self 了。

比如一些不会造成循坏引用的例子。

dispatch_async(dispatch_get_main_queue(), ^{
    [self doSomething];
});

self并没有对GCD的block进行持有,所以不会造成循环引用。

- (void)test:(void(^)())block{
    block();
}

 [self test:^{
    NSLog(@"%@",self.person);
  }];

这个并不会造成循环引用,此block只是一个临时变量,并不会对self造成引用。

3、

[UIView animateWithDuration:0.2 animations:^{
    self.alpha = 1;
}];

UIView 的某个负责动画的对象持有了 block,block 持有了 self。但因为 self 并不持有 block,所以就没有循环引用产生,因为就不需要使用 weakSelf。

那我们什么时候用到strongSelf呢

传进 block 之前,把 ‘self’ 转换成 weak automatic 的变量,这样在 block 中就不会出现对 self 的强引用。如果在 block 执行完成之前,self 被释放了,weakSelf 也会变为 nil。
在 block 中先写一个 strong self,其实是为了避免在 block 的执行过程中,突然出现 self 被释放的尴尬情况。通常情况下,如果不这么做的话,还是很容易出现一些奇怪的逻辑,甚至闪退。__strong确保在 block 内,strongSelf 不会被释放。
strongSelf 是一个局部变量,存在栈中,执行完这个block,strongSelf 就会自动释放。

当然我们也会看到strongSelf在block里的使用。

下面会用一个例子来说明:

 __weak typeof(person) weakPerson = person ;
 person.exmpleBlock = ^{
      NSLog(@"%@",weakPerson.name);
      [weakPerson text];
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@“name is = %@",weakPerson.name);
      });
  };
 person.exmpleBlock();

打印:name is = (null)

原因:exmpleBlock结束之后,person会被释放,在原对象释放之后,__weak对象就会变成null,
又由于dispatch_after里面捕获的weak的student,所以为防止野指针。所以就输出了null了

我们可以修改成这样:

__weak typeof(person) weakPerson = person ;
person.exmpleBlock = ^{
     NSLog(@"%@",weakPerson.name);
     [weakPerson text];
     __strong typeof(weakPerson) strongPerson = weakPerson;
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         NSLog(@“name is = %@",weakPerson.name);
      });
    };
person.exmpleBlock();

weakSelf 是为了block不持有self,避免Retain Circle循环引用。在block内如果需要访问self的方法、变量,建议使用 weakSelf。

strongSelf的目的是因为一旦进入block执行,假设不允许self在这个执行过程中释放或者在block内需要多次访问self,就需要使用strongSelf。

总结:

1 在 block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
2 如果在 block 内需要多次访问 self,则需要使用 strongSelf。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容