IOS 中block结构的简单用法

自从block出现之后,很多API都开始采用这样的结构,由此可见,block确实有许多优势存在,这里将一些简单用法总结如下:

一、如何声明一个block变量

我们通过^符号来声明block类型,形式如下:

void (^myBlock)();

其中第一个void是返回值,可以是任意类型,中间括号中^后面的是这个block变量的名字,我把它命名为myBlock,最后一个括号中是参数,如果多参数,可以写成如下样式:

int (^myBlock)(int,int);

同样,你也可以给参数起名字:

int (^myBlock)(int a,int b);

很多时候,我们需要将我们声明的block类型作为函数的参数,也有两种方式:

1、-(void)func:(int (^)(int a,int b))block;

第二种方式是通过typedef定义一种新的类型,这也是大多数情况下采用的方式:

2、typedef int (^myBlock)(int a,int b) ;

-(void)func:(myBlock)block ;

二、如何实现一个block

既然block可以被声明为变量,那么就一定可以实现它,就像其他类型变量的赋值。我自己对block的理解为它是一断代码块,所以给它赋值赋便是一段代码段:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

typedef int (^myBlock)(int,int) ;

@interface ViewController ()

{

    myBlock block1;

}

@end

 

@implementation ViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    block1 =^(int a, int b){

        return a+b;

    };

   NSLog(@"%d",block1(1,1));

}

这里打印的结果是2,从这里可以发现block和函数的功能很像。

注意:1、在上面的代码里 block1是一个对象,如果直接打印将打印对象地址

        2、block(),加上后面的括号才是执行block语句块

三、block中访问对象的微妙关系

1、如果你在一个block块中仅仅访问对象,而不是对他进行修改操作,是没有任何问题的:

?


1

2

3

4

5

6

7

8

9

10

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    int tem=2;

    block1 = ^(int a,int b){

        int count= tem+1;

        return count;

    };

    NSLog(@"%d",block1(1,1));

}

而如果我在block块中直接修改,编译器会报错:

?


1

2

3

4

  block1 = ^(int a,int b){

        tem+=1;

        return tem+1;

    };

为什么会出现这样的情况,根据猜测,可能是block内部将访问的变量都备份了一份,如果我们在内部修改,外部的变量并不会被修改,我们可以通过打印变量的地址来证明这一点:

?


1

2

3

4

5

6

7

8

9

10

- (void)viewDidLoad {

    [super viewDidLoad]; 

    int tem=2;

    NSLog(@"%p",&tem);

    block1 = ^(int a,int b){

        NSLog(@"%p",&tem);

        return tem+1;

    };

    NSLog(@"%d",block1(1,1)); 

}

打印结果如下:

可以看出,变量的地址已经改变。

2、__block 做了什么

为了可以在block块中访问并修改外部变量,我们常会把变量声明成__block类型,通过上面的原理,可以发现,其实这个关键字只做了一件事,如果在block中访问没有添加这个关键字的变量,会访问到block自己拷贝的那一份变量,它是在block创建的时候创建的,而访问加了这个关键字的变量,则会访问这个变量的地址所对应的变量。我们可以通过代码来证明:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

    int tem=2;

    block1 = ^(int a,int b){  

        return tem+a+b;

    };

    tem=4;

    NSLog(@"%d",block1(1,1));

     

    block1 = ^(int a,int b){  

        return tem+a+b;

    };

     __block int tem2=2;

    tem2=4;

     NSLog(@"%d",block1(1,1));

}

结果:

3、一点点扩展

由此,我们可以理解,如果block中操作的对象是指针,那么直接可以进行修改,这包括OC对象,如果不是,则需要用__block关键字修饰。

4、关于引用计数

在block中访问的对象,会默认retain:

?


1

2

3

4

5

6

7

    UIImage * number;

    number = [[UIImage alloc]init] ;

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

    block1 = ^(int a,int b){

        NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

    };

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

结果如下:

而添加__block的对象不会被retain;

注意:如果我们访问类的成员变量,或者通过类方法来访问对象,那么这些对象不会被retain,而类对象会被return,最常见的时self:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

typedef void(^myBlock)(int,int) ;

@interface ViewController2 ()

{

     myBlock block1;

     __block UIImage * number; 

}

@end

@implementation ViewController2

-(void)dealloc{

    NSLog(@"dealloc %@",self.class);

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

}

- (void)viewDidLoad {

    [super viewDidLoad];

    self.view.backgroundColor=[UIColor whiteColor];

    number = [[UIImage alloc]init] ;

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

    block1 = ^(int a,int b){

        NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

    };

    //block1(1,1);

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

     

    UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];

    btn.frame=CGRectMake(100, 100, 100, 100);

    btn.backgroundColor=[UIColor redColor];

    [self.view addSubview:btn];

    [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

}

-(void)click{

    [self dismissViewControllerAnimated:YES completion:nil];

}

打印结果:

可以看出,UIImage对象没有被retain,而self也将循环引用,造成内存泄露。解决方法如下:

?


1

2

3

4

5

6

7

 number = [[UIImage alloc]init] ;

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

    UIImage * im = number;

    block1 = ^(int a,int b){

        NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)im));

    };

    NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));

打印结果:

注意:根据这个机制,如果我们将block用来传值,在block不用时,务必要置为nil,而在实现block的方法里,务必要释放;我们通过代码来解释:

首先,创建三个ViewController,为ViewController1,ViewController2,ViewController3;

1、在ViewController1中创建一个按钮,跳转ViewController2

2、在ViewController2中:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

#import "ViewController2.h"

#import "ViewController3.h"

@interface ViewController2 ()

{

     UIButton * im;

}

@end

 

@implementation ViewController3

-(void)dealloc{

    NSLog(@"dealloc %@",self.class);

}

- (void)viewDidLoad {

    [super viewDidLoad];

    UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];

    btn.frame=CGRectMake(300, 300, 100, 100);

    btn.backgroundColor=[UIColor redColor];

    [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:btn];

    im = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

    im.backgroundColor=[UIColor blackColor];

    [im addTarget:self action:@selector(rele) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:im];

}

-(void)rele{

    [self dismissViewControllerAnimated:YES completion:nil];

}

-(void)click{

    ViewController3 * con = [[ViewController3 alloc]init];

    [con setBlock:^{

        im.backgroundColor=[UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];

    }];

    [self presentViewController:con animated:YES completion:nil];

}

3、在ViewController3中:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

#import "ViewController3.h"

void (^myBlock)();

@implementation ViewController3

-(void)setBlock:(void(^)())block{

    myBlock = [block copy];

}

-(void)dealloc{

    NSLog(@"dealloc %@",self.class);

}

- (void)viewDidLoad {

    [super viewDidLoad];

    self.view.backgroundColor=[UIColor whiteColor];

    myBlock();   

    UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];

    btn.frame=CGRectMake(100, 100, 100, 100);

    btn.backgroundColor=[UIColor redColor];

    [self.view addSubview:btn];

    [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];

}

-(void)click{

    [self dismissViewControllerAnimated:YES completion:nil];

}

通过打印信息,我们会发现,ViewController2不被释放,原因是其成员变量im被block中retain没有释放,我们这样做:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@interface ViewController2 ()

{

    UIButton * im;

    ViewController3 * tem;

}

-(void)rele{

    [tem setBlock:nil];

    [self dismissViewControllerAnimated:YES completion:nil];

}

-(void)click{

    ViewController3 * con = [[ViewController2 alloc]init];

    tem=con;

    [con setBlock:^{

        im.backgroundColor=[UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];

    }];

    [self presentViewController:con animated:YES completion:nil];

}

这样就解决了内存问题。

四、关于block的作用域

应避免将花括号中的block用于外面,如果需要,你可以将这个block声明为全局的。

时间: 2024-08-22 15:17:49

IOS 中block结构的简单用法的相关文章

iOS中Block的回调使用和解析详解_IOS

Block 回调实现 先跟着我实现最简单的 Block 回调传参的使用,如果你能举一反三,基本上可以满足了 OC 中的开发需求.已经实现的同学可以跳到下一节. 首先解释一下我们例子要实现什么功能(其实是烂大街又最形象的例子): 有两个视图控制器 A 和 B,现在点击 A 上的按钮跳转到视图 B ,并在 B 中的textfield 输入字符串,点击 B 中的跳转按钮跳转回 A ,并将之前输入的字符串 显示在 A 中的 label 上.也就是说 A 视图中需要回调 B 视图中的数据. 想不明白的同学

iOS中sqlite数据库的原生用法_IOS

在iOS中,也同样支持sqlite.目前有很多第三方库,封装了sqlite操作,比如swift语言写的SQLite.swift.苹果官网也为我们封装了一个框架:CoreData. 它们都离不开Sqlite数据库的支持. 本文主要介绍下,如何在swift中使用原生的sqlite的API. 在Xcode中引入sqlite API 新建一个swift项目后,我们需要让项目引入sqlite的动态链接库: 1.项目配置界面,选择Build Phases 2.点开Link Binary With Libra

iOS中block类型大全

iOS中block类型大全 typedef的block 作为属性的block   作为变量的block   作为方法变量入参的block   作为方法参数的block   无名block   内联函数的block   递归调用的block   作为方法返回值的block   作为函数名的block(太过奇葩,完全不知道怎么用-_-!)

Android中WebView的一些简单用法_Android

Android中WebView的一些简单用法 一直想写一个关于 WebView 控件的 一些简单运用,都没什么时间,这次也是挤出时间写的,里面的一些基础知识就等有时间再更新讲解一下,今天就先把项目出来做一些简单介绍,过多的内容可以看我的源码,都传到github上了. 下面是项目的效果图: 应用用到的是 MVP 设计模式,对这种模式还不太了解的可以先自行google一下,不然项目估计会看的晕,虽然我的代码都很简洁的. 对于MVP 可以带着一个思路看源码,那就是 activity(或其他组件)通过

Java中BigDecimal类的简单用法_java

本文实例讲述了Java中BigDecimal类的简单用法,是Java程序设计中非常实用的技巧,分享给大家供大家参考.具体用法分析如下: 一般来说,一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法进行精确计算.但是Java的设计者给编程人员提供了一个很有用的类BigDecimal,他可以完善float和double类无法进行精确计算的缺憾.BigDecimal类位于java.maths类包下.首先我们来看下如何构造一个BigDecimal对象.它的构造函数很多,

Android中WebView的一些简单用法

Android中WebView的一些简单用法 一直想写一个关于 WebView 控件的 一些简单运用,都没什么时间,这次也是挤出时间写的,里面的一些基础知识就等有时间再更新讲解一下,今天就先把项目出来做一些简单介绍,过多的内容可以看我的源码,都传到github上了. 下面是项目的效果图: 应用用到的是 MVP 设计模式,对这种模式还不太了解的可以先自行google一下,不然项目估计会看的晕,虽然我的代码都很简洁的. 对于MVP 可以带着一个思路看源码,那就是 activity(或其他组件)通过

iOS中block实现的探究

[0. Brief introduction of block] Block是iOS4.0+ 和Mac OS X 10.6+ 引进的对C语言的扩展,用来实现匿名函数的特性. 用维基百科的话来说,Block是Apple Inc.为C.C++以及Objective-C添加的特性,使得这些语言可以用类lambda表达式的语法来创建闭包. 用Apple文档的话来说,A block is an anonymous inline collection of code, and sometimes also

iOS中视频播放器的简单封装详解_IOS

前言 如果仅仅是播放视频两者的使用都非常简单,但是相比MediaPlayer,AVPlayer对于视频播放的可控制性更强一些,可以通过自定义的一些控件来实现视频的播放暂停等等.因此这里使用AVPlayer的视频播放. 视频播放器布局 首先使用xib创建CLAVPlayerView继承UIView用来承载播放器,这样我们在外部使用的时候,直接在控制器View或者Cell上添加CLAVPlayerView即可,至于播放器播放或者暂停等操作交给CLAVPlayerView来管理.下面来看一下CLAVP

iOS中的UISlider滑块组件用法总结_IOS

PC上的滑块是很丑陋的,因为我们只能通过鼠标去拖动他.不过当Jobs把它移植到IOS上时一切变得酷起来,因为我们可以通过手指去拖动它,这种感觉是很妙的. 滑块为用户提供了一种可见的做范围调整的方法,用户可以通过拖动一个滑动条改变它的值,并且可以对其配置以合适不同值域.你可以设置滑块值的范围,也可以在两端加上图片,以及进行各种调整让它更美观.滑块非常适合用于表示在很大范围(但不精确)的数值中进行选择,比如音量设置.灵敏度控制等诸如此类的用途.一.创建滑块是一个标准的UIControl.我们可以通过