谈谈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();
- 无返回值、有参数的block
void (^exmpleBlock)(int a,int b)=^(int a,int b){
NSLog(@"sum: %d",a+b);
};
exmpleBlock(1,2);
- 有返回值、有参数的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。