block的使用和注意

                         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底层源码实现的讲解,讲的很透彻,分析的很好!



参考文献

iOS中Block的基础用法

一篇文章看懂iOS代码块Block

你可能感兴趣的:(block的使用和注意)