Block详解

1️⃣Block的修饰

ARC情况下(

ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。

该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 可以使用该机能。简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。有一点,ARC并不是GC,它只是一种代码静态分析(Static Analyzer)工具。

1.如果用copy修饰Block,该Block就会存储在堆空间。则会对Block的内部对象进行强引用,导致循环引用。内存无法释放。

解决方法:

新建一个指针(__weak typeof(Target) weakTarget = Target )指向Block代码块里的对象,然后用weakTarget进行操作。就可以解决循环引用问题。

2.如果用weak修饰Block,该Block就会存放在栈空间。不会出现循环引用问题。

MRC情况下

用copy修饰后,如果要在Block内部使用对象,则需要进行(__block typeof(Target) blockTarget = Target )处理。在Block里面用blockTarget进行操作。

2️⃣Block的定义格式

返回值类型(^block变量名)(形参列表) = ^(形参列表) {

};

默认情况下,Block内部不能修改外面的局部变量

Block内部可以修改使用__block修饰的局部变量

3️⃣Block的模式

1.无参数无返回值的Block

2.有参数无返回值的Block

3.有参数有返回值的Block

4️⃣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"));

5️⃣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];

}

=========================================================================================

1.block简介

在 iOS中, block一共分三种。

(1)全局静态 block,不会访问任何外部变量,执行完就销毁。

^{

        NSLog(@"Hello World!");

    }();

(2)保存在栈中的 block,当函数返回时会被销毁,和第一种的区别就是调用了外部变量。

    [UIView animateWithDuration:3 animations:^{


        self.view.backgroundColor = [UIColor redColor];

    }];

(3)保存在堆中的 block,当引用计数为 0 时会被销毁。例如按钮的点击事件,一直存在,即使执行过,也不销毁,因为按钮还可能被点击,持有按钮的View被销毁,它才会被销毁。

#import

typedef void(^ButtonClickBlcok)();

@interface TestView : UIView

@property (nonatomic, copy) ButtonClickBlcok buttonClickBlcok;

@end

#import "TestView.h"

@implementation TestView

- (IBAction)buttonClick:(id)sender {


    if (self.buttonClickBlcok) {

        self.buttonClickBlcok();

    }

}

@end

2.block优点

block的代码可读性更好。因为应用block和实现block的地方在一起。代理的声明和实现就分开来了,在两个类中。代理使用起来也更麻烦,因为要声明协议、声明代理、遵守协议、实现协议里的方法。block不需要声明,也不需要遵守,只需要声明和实现就可以了。

block是一种轻量级的回调,可以直接访问上下文,由于block的代码是内联的,运行效率更高。block就是一个对象,实现了匿名函数的功能。所以我们可以把block当做一个成员变量、属性、参数使用,使用起来非常灵活。像用AFNetworking请求数据和GCD实现多线程,都使用了block回调。

3.block缺点

blcok的运行成本高。block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是引用计数加1,使用完或者block置nil后才销毁。delegate只是保存了一个对象指针(一定要用week修饰delegate,不然也会循环引用),直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。

block容易造成循环引用,而且不易察觉。因为为了blcok不被系统回收,所以我们都用copy关键字修饰,实行强引用。block对捕获的变量也都是强引用,所以就会造成循环引用。

#import "ViewController.h"

typedef void(^TestBlock)(void);

@interface ViewController ()

{

    void (^_testCycleBlock)(void);

}

@end

@implementation ViewController

- (void)viewDidLoad {


    [super viewDidLoad];


    __weak ViewController *weakSelf = self;

    _testCycleBlock = ^{

        /**

          //引发循环引用

          NSLog(@"%@", self);

        */

        //防止循环引用

        NSLog(@"%@", weakSelf);

    };

}

@end

4.如何使用

优先使用block。

如果回调函数很多,多余三个使用代理。

如果回调的很频繁,次数很多,像UITableview,每次初始化、滑动、点击都会回调,使用代理。

  block和代理都各有优缺点,所以我们一定要理解区分使用场景,应用适合的回调方式。优化APP的性能,提高流畅性,从点滴做起。

你可能感兴趣的:(Block详解)