浅谈ios之block的基础

在iOS 4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。这不免让我们想到在C函数中,我们可以定义一个指向函数的指针并且调用:

bool executeSomeTask(void) {

//do something and return if success or not

}

bool (*taskPoint)(void);

taskPoint = something;

上面的函数指针可以直接通过(*taskPoint)()的方式调用executeSomeTask这个函数,这样对比block跟似乎C语言的函数指针是一样的,但是两者仍然存在以下区别:

block的代码是内联的,效率高于函数调用

block对于外部变量默认是只读属性

block被Objective-C看成是对象处理

小结:

1.  block 是 C 语言的

2. block 是一种匿名函数

3. 是一种数据类型,可以当作参数传递

4. 是一组预先准备好的代码,在需要的时候执行

Block 应用场景:

1. 自定义视图的反向传值

2. Modal / POP 控制器的反向传值

3. 异步方法执行完毕后的反向传值

注:

1. Block 的反向传值一般被称为回调

2. 反向传递的树枝通过 Block 的参数传递

Block 的定义和匿名函数

定义 block 的函数体

1. 以^起始,表示是一个block

2. 以{}包装block中的代码块

^ {

NSLog(@"hello block");   

};

提示:Expression result unused=> 表达式没有使用

使用一个变量记录 block

// block 是一个没有参数,没有返回值的匿名函数

// myBlock 是记录这个匿名函数的变量名

void(^myBlock)() = ^ {

NSLog(@"hello block");   

};

提示:Unused variable 'myBlock'=>'myBlock' 没有使用

使用变量

// block 是一个没有参数,没有返回值的匿名函数

// myBlock 是记录这个匿名函数的变量名

void(^myBlock)() = ^ {

NSLog(@"hello block");    

};

// 执行 myBlock 变量中记录的代码myBlock();

Block 的定义可以借助inlineBlock速记,但是:

1. 不要过份依赖inlineBlock

2. block的手写定义一定要过关!

当做参数传递 Block

定义一个接收并且执行 block 的方法

// 接收并且执行 block

- (void)callBlock:(void(^)())completion {

NSLog(@"干点什么");    

completion();

}

定义 block 并且当做参数传递

#pragma mark - Block 当做参数传递

- (void)blockDemo2 {

void(^myBlock)() = ^ {

NSLog(@"hello block");    

};    

[selfcallBlock:myBlock];

}

通过 Block 的参数回调

准备一个方法,模拟回调网络请求结果

//  通过 block 的参数回调模拟网络请求的结果

- (void)callBlock2:(void(^)(NSString*result))completion {

NSLog(@"网络加载了一点数据");

NSString*jsonString =@"我是网络加载的 json 字符串";    

completion(jsonString);

}

定义 block 并且传递参数

#pragma mark - Block 通过参数回调

- (void)blockDemo3 {

// 1. 定义 blockvoid(^myBlock)(NSString*) = ^ (NSString*json) {

NSLog(@"%@", json);

    };

// 2. 调用方法

[selfcallBlock2:myBlock];

}

block的反向传值

来一个小demo:

点击PUSH按钮在最右侧控制器输入用户名

点击保存按钮 POP 控制器,并且将用户名显示在nameLabel中

项目准备

右侧的 DemoViewController

在DemoViewController中定连线属性

@interface DemoViewController()

@property(weak,nonatomic)IBOutlet UITextField *nameText;

@end

连线方法

/// 保存并返回

- (IBAction)save:(id)sender {

// 将输入内容传回

// 导航控制器弹栈

[self.navigationController popViewControllerAnimated:YES];

}

在 .h 中定义属性

/// 输入完成 block 回调

@property(nonatomic,copy)void(^inputCompletion)(NSString*userName);

修改 save 方法

// 保存并返回

- (IBAction)save:(id)sender {

// 判断 block 属性是否有内容

if(self.inputCompletion != nil) {

// 执行完成回调将输入内容传回

self.inputCompletion(_nameText.text);

    }

// 导航控制器弹栈

[self.navigationController popViewControllerAnimated:YES];

}

左侧的 viewController

导入头文件

#import"DemoViewController.h"

属性连线

@property(weak,nonatomic)IBOutlet UILabel*nameLabel;

实现prepareForSegue:

- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender {

// 1. 获取目标控制器

DemoViewController *vc = segue.destinationViewController;

// 2. 设置完成回调

[vc setInputCompletion:^(NSString*name) {

self.nameLabel.text= name;

    }];

}

与代理的对比

实现步骤的对比

被调用方(子控制器)

代理

定义协议

定义代理属性

需要的时候通知代理执行协议方法

block

定义 block 属性

需要的时候执行 block 属性

调用方

代理

设置代理

遵守协议

实现协议方法

block

设置 block 属性,准备需要执行的代码

代码实现的对比

Block 的优势

使用 block 所有的代码写在一起,更加便于维护和阅读

使用 代理 代码想对松散

Block 的弱势

一个回调对应一个属性,属性定义在类的头文件中,容易和其他属性混在一起

代理设计模式通过协议预先定义好代理的行为,从设计模式而言,更加严谨

尤其在协议方法很多的时候,使用代理更加容易上手!

使用 block 需要注意循环引用!

block 保存的内存区域

不引用任何外部变量的 block

- (void)blockDemo1 {

void(^myBlock)() = ^ {

NSLog(@"hello world");

    };

NSLog(@"%@", myBlock);

}

不引用任何外部变量的 block 保存在全局区__NSGlobalBlock__

从而可以保证无论 block 如何被传递,内部包装的代码都不会发生变化,而且执行效率高

但是实际开发中,不引用外部变量的 block 几乎是不存在的

引用外部变量的 block

- (void)blockDemo2 {

int i =10;

void(^myBlock)() = ^ {

NSLog(@"hello world %zd", i);

    };

NSLog(@"%@", myBlock);

}

引用外部变量的 block 保存在:

ARC:堆区__NSMallocBlock__

MRC:栈区__NSStackBlock__

因此:在定义block 属性时应该使用copy关键字,将 block 从栈区复制到堆区

定义 block 属性

/// 定义 block 属性

@property(nonatomic,copy)void(^demoBlock)();

记录 block 属性

- (void)blockDemo2 {

int i =10;

void(^myBlock)() = ^ {

NSLog(@"hello world %zd", i);

    };

NSLog(@"%@", myBlock);

// 错误的写法,不会调用 setter 方法

// _demoBlock = myBlock;

// 正确的写法,调用 setter 方法,并且对 block 进行 copy

self.demoBlock= myBlock;

NSLog(@"%@",self.demoBlock);

}

提示:为了避免程序员的麻烦,在 ARC 中,定义了引用外部变量的 block,默认都是在堆区的!

block的循环引用

定义一个引用self.的 block

@implementation ViewController

- (void)viewDidLoad {

    [superviewDidLoad];

void(^block)() = ^ {

NSLog(@"%@",self.view);

    };

    block();

}

- (void)dealloc {

NSLog(@"%s", __FUNCTION__);

}

@end

运行测试

没有循环引用:因为 block 执行完毕后会释放,从而释放对self的引用

定义属性

@property(nonatomic,copy)void(^demoBlock)();

在viewDidLoad中记录 block

- (void)viewDidLoad {

    [superviewDidLoad];

void(^block)() = ^ {

NSLog(@"%@",self.view);

    };

// 记录 block,等待需要的时候执行

self.demoBlock= block;

}

运行测试

循环引用发生了:

因为ViewController引用了block属性

block内部引用了self(ViewController)

相互引用产生了循环引用

解决循环引用

使用__weak修饰符,定义一个弱引用的对象

然后让block引用此弱引用对象,从而可以打破循环引用

__weak typeof(self) weakSelf =self;

void(^block)() = ^ {

NSLog(@"%@", weakSelf.view);

    };

提示:

在使用block的时候,如果出现self,同时使用属性记录 block 的时候,要格外注意是否会出现循环引用

注意:不是所有的self.都会出现循环引用 ——block 执行完毕就销毁,例如 UIView 的动画代码

大坑:在block内部不要使用_成员变量

因为,成员变量默认是由控制器强引用的,以下代码同样会出现循环引用,同时非常不容易发现!

@implementation ViewController{

NSMutableArray*_arrayM;

}

- (void)viewDidLoad {

    [superviewDidLoad];

    __weak typeof(self) weakSelf =self;

void(^block)() = ^ {

NSLog(@"%@ %@", weakSelf.view, _arrayM);

    };

// 记录 block,等待需要的时候执行

self.demoBlock= block;

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • Block使用场景,可以在两个界面的传值,也可以对代码封装作为参数的传递等。用过GCD就知道Block的精妙之处。...
    Coder_JMicheal阅读 716评论 2 1
  • iOS代码块Block 概述 代码块Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,B...
    smile刺客阅读 2,328评论 2 26
  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,757评论 0 23
  • 简述 一句话搞懂block:可以理解为,block是对上下文代码段的打包,然后在适当的时机执行。 block长什么...
    Allan_野草阅读 2,154评论 0 25
  • 《Objective-C高级编程》这本书就讲了三个东西:自动引用计数、block、GCD,偏向于从原理上对这些内容...
    WeiHing阅读 9,793评论 10 69