iOS学习-(1)block的使用和注意
本文简介
本文主要介绍1.block的基本使用 2.block使用中的注意(循环引用)
Block简介
代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性。
Block是一种比较特殊的数据类型。和int float 类型一样是一种数据类型,其可以正常定义变量、作为参数、作为返回值,等 但是,特殊地,它可以保持一段代码,在合适的时候取出来调用,也就是存储了一个指向保持代码的地址,因此它可以像指针一样copy保持的一段代码。因此总结block 形式如int普通类型,但是实际作用更像指针。
目前Block已经广泛应用于iOS开发中,常用于GCD、动画、排序及各类回调。
现在我们主要用的就是他的各类回调(页面传值)
//动画([UIView animateWithDuration:0.3 animations:^{)
}];
注: Block的声明与赋值只是保存了一段代码段,必须调用才能执行内部代码
block的定义和使用
block 定义
返回值类型(^block变量名)(形参列表) = ^(形参列表) {
};
调用Block保存的代码
block变量名(实参);
因为Block 保存的是一段代码 所以有返回值和形参列表就很正常了
使用typedef定义Block类型
在实际使用Block的过程中,我们可能需要重复地声明多个相同返回值相同参数列表的Block变量,如果总是重复地编写一长串代码来声明变量会非常繁琐,所以我们可以使用typedef来定义Block类型
// 定义一种无返回值无参数列表的Block类型
typedef void(^SayHello)();
// 我们可以像OC中声明变量一样使用Block类型SayHello来声明变量
SayHello hello = ^(){
NSLog(@"hello");
};
// 调用后控制台输出"hello"
hello();
Block的模式
1.无参数无返回值的Block
2.有参数无返回值的Block
3.有参数有返回值的Block
Block简单用法举例
无参数无返回值的Block
/**
* 无参数无返回值的Block
*/
-(void)func1{
/**
* void :就是无返回值
* emptyBlock:就是该block的名字
* ():这里相当于放参数。由于这里是无参数,所以就什么都不写
*/
void (^emptyBlock)() = ^(){
NSLog(@"无参数,无返回值的Block");
};
emptyBlock();
}
有参数无返回值的Block
/**
* 调用这个block进行两个参数相加
*
* @param int 参数A
* @param int 参数B
*
* @return 无返回值
*/
void (^sumBlock)(int ,int ) = ^(int a,int b){
NSLog(@"%d + %d = %d",a,b,a+b);
};
/**
* 调用这个sumBlock的Block,得到的结果是20
*/
sumBlock(10,10);
有参数有返回值的Block
/**
* 有参数有返回值
*
* @param NSString 字符串1
* @param NSString 字符串2
*
* @return 返回拼接好的字符串3
*/
NSString* (^logBlock)(NSString *,NSString *) = ^(NSString * str1,NSString *str2){
return [NSString stringWithFormat:@"%@%@",str1,str2];
};
//调用logBlock,输出的是 我是Block
NSLog(@"%@", logBlock(@"我是",@"Block"));
示例
Block结合typedef使用自己定义一个Block类型,用定义的类型去创建Block,更加简单便捷。这里举例一个Block回调修改上一下界面的背景颜色。ViewController1 控制器1,ViewController2 控制器2控制器1跳转到控制器2,然后在控制器2触发事件回调修改控制器1的背景颜色为红色。
ViewController2的实现
#import@interface ViewController2 : UIViewController
/**
* 定义了一个changeColor的Block。这个changeColor必须带一个参数,这个参数的类型必须为id类型的
* 无返回值
* @param id
*/
typedef void(^changeColor)(id);
/**
* 用上面定义的changeColor声明一个Block,声明的这个Block必须遵守声明的要求。
*/
@property (nonatomic, copy) changeColor backgroundColor;
@end
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
//声明一个颜色
UIColor *color = [UIColor redColor];
//用刚刚声明的那个Block去回调修改上一界面的背景色
self.backgroundColor(color);
}
ViewController1的实现
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event{
ViewController2 *vc =[[ViewController2 alloc]init];
// 回调修改颜色
vc.backgroundColor = ^(UIColor *color){
self.view.backgroundColor = color;
};
[self.navigationController pushViewController:vc animated:YES];
}
Block注意 (循环引用)
//需要明白的
1.ARC中声明的局部变量的对象是一个强引用
2.对象消亡的条件是没有一个强引用指向这个对象
3.对象没有强引用,不会马上消亡
结论:
1.如果在^(){
block内部 引用了在外部声明的强引用【对象A】,则在block内部自动对产生一个强引用来指向【对象A】
block内部 引用了在外部声明的弱引用【对象A】,则在block内部自动对产生一个弱引用来指向【对象A】
如果嵌套的block的最里面的block 有引用最外部的强引用【对象A】,则有通透现象发生,所有外部的Block也都有强引用指向该【对象A】
};
实现方法
如果B对象有强引用A对象,而对象A 有属性(copy)block,如果对象B在A中的Block被强引用,则导致导致A和B循环引用,进而引起内存泄漏,一般在block内应用的对象 都要在外部声明一个弱引用
如
mTopView = [[BBSTopButtonView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, 45) configDic:nil];
__weak SwimmerBBSViewController *weakSelf = self;
mTopView.block = ^(NSInteger type) {
if (type == 100) {
mType = 8888;
weakSelf.mTableView.hidden = YES;
weakSelf.mCollectionView.hidden = NO;
}else{
mType = type-100;
weakSelf.mTableView.hidden = NO;
weakSelf.mCollectionView.hidden = YES;
}
mPage = 1;
[weakSelf getData];
};
[self.view addSubview:mTopView];
分析:
和实现方法对应:
self为B对象
mTopView 为self中的全局变量 (强引用)A对象
循环引用比较简单,造成循环引用的原因无非就是对象和block相互强引用,造成谁都不能释放,从而造成了内存泄漏。基本的一些例子我就不再重复了,网上很多,也比较简单,我就一个问题来讨论下,也是开发中有人问过我的一个问题:
block里面使用self会造成循环引用吗?
很显然答案不都是,有些情况下是可以直接使用self的,比如调用系统的方法:
1
2
3
[UIView animateWithDuration:0.5 animations:^{
NSLog(@"%@", self);
}];
因为这个block存在于静态方法中,虽然block对self强引用着,但是self却不持有这个静态方法,所以完全可以在block内部使用self。
还有一种情况:
当block不是self的属性时,self并不持有这个block,所以也不存在循环引用
1
2
3
4
void(^block)(void) = ^() {
NSLog(@"%@", self);
};
block();
只要我们抓住循环引用的本质,就不难理解这些东西。
最后附上巧神对Block底层源码实现的讲解,讲的很透彻,分析的很好!