iOS开发系列-Block
概述
在iOS 4.0之后,block横空出世,它本身封装了一段代码并将这段代码当做变量,通过block()的方式进行回调。这不免让我们想到在C函数中,我们可以定义一个指向函数的指针并且调用。
#import <Foundation/Foundation.h>
void function(){
NSLog(@"function执行了");
}
int main(int argc, const char * argv[]) {
void(*funcP)(void) = function;
// 函数指针调用函数
funcP();
return 0;
}
Block的本质就是函数指针。只要通过函数指针可以在任何时候执行函数
。
Block基本使用
Block的类型
block也是一种数据类型,Block的类型是什么呢。
返回值类型(^)(参数类型列表)
就是Block的类型。开发中可以利用typedef定义同一种类型的Block。
// 定义一个没有返回值没有形参的Block类型MyBlock
typedef void(^MyBlock)(void);
Block的声明定义
声明一个block类型的变量
返回值类型(^blockName)(参数类型列表)
定义一个block
^返回值类型(参数列表) {
};
Block的定义不管有没有返回值,在定义时返回值类型可以省略。当一个block没有参数时Block定义^后面的括号也可以省略。
// 标准的声明与定义一个block
void (^block)() = ^void(){
};
// 定义省略返回值
void (^block)() = ^(){
};
// block没有返回值省略^后面的括号
void (^block)() = ^{
};
Block的使用场景
监听逆向传值
开发中我们通常使用代理来做监听并且逆向传值,其实使用Block也可以做到。给被监听着添加一个Block属性,在外界给被监听着赋值block。当被监听着内部发生了事件想通知给外界可以执行属性block。通过Block的参数将值传递出来。
其实本质就是Block就是一个函数指针,block定义其实就是一个定义函数的实现。当执行block,就通过block指针地址找到方法实现执行函数。这个Block定义在监听者内部,当被监听者内部执行了block就等于执行了监听者内部定义的函数。
Block的内存管理
MRC下Block的内存管理
在MRC中Block在内存中的位置是有多种情况,总体分为三种.
* NSGlobalBlock
* NSStackBlock
* NSMallocBlock
在MRC定义一个Block,对Block进行控制台输出发现Block默认是在全局区
的。
void (^block)(void) = ^void(){
NSLog(@"----------------");
};
NSLog(@"%@", block);
当Block内部访问了局部变量,Block是在栈区
。如果访问外部的变量静态变量或者全局变量,Block还是保存在全区区。
int a = 12;
void (^block)(void) = ^void(){
NSLog(@"----------------%d", a);
};
当一个栈区
的Block通过copy后会生成新的Block,此时的Block存储在堆区。全局区的Block被copy后没有生成新的Block。
int a = 10;
void (^block)(void) = ^void(){
// 访问了外界的局部变量a block就保存在栈区
NSLog(@"----------------%d", a);
};
NSLog(@"%@", block);
NSLog(@"%@", [block copy]);
这就是为什么在MRC下,Block属性Propery会使用copy。如果使用的是retain那么block在栈区过了作用域就会释放,当调用者属性block时发生坏访问。
如果是MRC声明了一个copy修饰的属性,建议在对象的dealloc的方法对所拥有的Block进行release。
- (void)dealloc
{
[self.block release];
[super dealloc];
}
ARC下Block的内存管理
在ARC下默认定义Block同样存储在全局区
,不同的是在ARC下,Block内部引用了局部变量是存储在堆区的。
static int a = 10;
void (^block)(void) = ^void(){
NSLog(@"----------------%d", a); // <__NSMallocBlock__: 0x604000241860>
};
在ARC下如果声明一个Block属性property修饰符建议使用strong。如果使用的copy跟strong的作用一样用一个强指针引用着Block。但是copy内部需要做一些逻辑处理,为了性能建议使用strong。
Block循环引用
在FMModelVCViewController控制器中声明一个block属性。在viewDidLoad中创建一个block为属性赋值。block内部输出self
。
@interface FMModelVCViewController ()
/** block属性 */
@property (nonatomic, strong) void(^block)(void);
@end
@implementation FMModelVCViewController
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)(void) = ^{
NSLog(@"----------------%@", self);
};
self.block = block;
}
Block会对里面的所有外部强指针变量进行强引用。
上面的代码就造成了循环引用,在内存中控制器不会销毁。
解决方案
在Block内部访问控制器__weak
修饰指针的。
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
void(^block)(void) = ^{
NSLog(@"----------------%@", weakSelf);
};
self.block = block;
}
这样Block对控制器强产生的是弱引用。
Block中延时任务问题
当Block中有延时操作,延时操作block中想访问外界的对象,但是通常Block为了防止循环引用使用是_weak修饰的对象指针。当Block内部的延时Block访问的weak修饰的对象也是弱引用。有可能造成当执行延时的Block时,其内部引用的外部对象已经销毁。
#import "FMModelVCViewController.h"
@interface FMModelVCViewController ()
/** block属性 */
@property (nonatomic, strong) void(^block)(void);
@end
@implementation FMModelVCViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
void(^block)(void) = ^{
// afterBlock由系统管理
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"----------------%@", weakSelf);
});
};
self.block = block;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 执行block
self.block();
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
当点击屏幕时控制器执行了dismissViewControllerAnimated控制器方法控制器销毁,2s过后执行延时Block是输出null;
此时的内存如下:
解决方案
对Block内部代码调整如下
这样2s之后依然可以访问到控制器,当延时Block执行完毕。控制器才销毁。至于为什么通过内存图你就明白了。
iOS开发系列-Block的更多相关文章
- iOS开发系列-Block本质篇
概述 在iOS开发中Block使用比较广泛,对于使用以及一些常规的技术点这里不再赘述,主要利用C++角度分析Block内部数据底层实现,解开开发中为什么这样编写代码解决问题. Block底层结构窥探 ...
- iOS开发系列--Swift进阶
概述 上一篇文章<iOS开发系列--Swift语言>中对Swift的语法特点以及它和C.ObjC等其他语言的用法区别进行了介绍.当然,这只是Swift的入门基础,但是仅仅了解这些对于使用S ...
- iOS开发系列--通知与消息机制
概述 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情.iOS中通知机制又叫消息机制,其包括两类:一类是本地 ...
- iOS开发系列--数据存取
概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储到数据库.例如前面IOS开发系列-Objective-C之Foundation框架的文章中提到归档.plist文件存储, ...
- iOS开发系列--让你的应用“动”起来
--iOS核心动画 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建 ...
- iOS开发系列--并行开发其实很容易
--多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的, ...
- iOS开发系列--让你的应用“动”起来
--iOS核心动画 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌.在这里你可以看到iOS中如何使用图层精简非交互式绘图,如何通过核心动画创建 ...
- 【转】iOS开发系列--数据存取
原文: http://www.cnblogs.com/kenshincui/p/4077833.html#SQLite 概览 在iOS开发中数据存储的方式可以归纳为两类:一类是存储为文件,另一类是存储 ...
- IOS开发系列 --- 核心动画
原始地址:http://www.cnblogs.com/kenshincui/p/3972100.html 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥i ...
随机推荐
- react 编写 基于ant.design 页面的参考笔记
前言 因为我没有系统的学习 react,是边写边通过搜索引擎找相对的问题,看 ant.design的 中文文档 编写的一个单页面, 以下的笔记都是写 gksvideourlr 时记录的. 重新设定表单 ...
- Python3.5-20190518-廖老师-自我笔记-面向对象
面向对象编程,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 把老师分为一类,把学生分为一类.具体到某一个学生就是 这类中的一个具体对象,对象的 ...
- Shell脚本并发及并发数的控制
https://www.jianshu.com/p/701952ffb755 正常情况下,Shell脚本是串行执行的,一条命令执行完才会执行接下来的命令.如下代码: # !/bin/bash for ...
- MySQL图形化管理工具之Navicat安装以及激活
软件以及激活包下载地址 1. 安装navicat 双击navicat_trial_11.1.20.0.1449226634.exe,一路下一步安装(记住安装目录) 2. 激活 双击PatchNavic ...
- 【dart学习】-- Dart之JSON
概述 现在很难想象移动应用程序不需要与后台交互或者存储结构化数据.现在开发,数据传输方式基本都是用JSON,在Flutter中是没有GSON/Jackson/Moshi这些库,因为这些库需要运行时反射 ...
- paper 138:qt安装及问题解决
学习了很久的QT,遇到很多很多的问题,下面总结一下安装过程中遇到的问题吧, 1 下载QT 1)进入官网:https://www.qt.io/ 2)点击Download:https://www.qt.i ...
- BZOJ 1095: [ZJOI2007]Hide 捉迷藏(动态点分治)
传送门 解题思路 点分树其实就是在点分治的基础上,把重心连起来.这样树高是\(log\)的,可以套用数据结构进行操作.这道题是求最远距离,所以每个点维护两个堆,分别表示所管辖的子树的最远距离和到父节点 ...
- BZOJ 5415: [Noi2018]归程(kruskal重构树)
解题思路 \(NOI2018\)的\(Day1\) \(T1\),当时打网络赛的时候不会做.学了一下\(kruskal\)重构树后发现问题迎刃而解了.根据\(kruskal\)的性质,如果要找从\(u ...
- 6.zabbix微信告警3.2
原文地址: https://blog.cactifans.com/2016/01/27/zabbix%E5%BE%AE%E4%BF%A1%E5%91%8A%E8%AD%A6/ pdf : 链接: ht ...
- ssh登录失败的常见问题分析
操作系统为了安全,一般只允许普通用户使用public_key登录,这时如果以root用户登录,就会出现各种错误.下面是常见的错误及解决方案. Permission denied (publickey) ...