【Effective Objective - C】—— block 块

news2024/11/27 0:30:41

【Effective Objective - C】—— block 块

  • 前言
  • 37.理解块的概念
    • 块的基础知识
    • 块可以捕获变量
    • 内联块的用法
    • 块的内部结构
    • 栈块
    • 堆块
    • 全局块
    • 要点
  • 38.为常用的块类型创建typedef
    • 要点
  • 39.用handler块降低代码分散程度
    • 协议传值实现异步
    • 块实现异步
    • 回调操作里的块
    • 要点
  • 40.用块引用其所属对象时不要出现保留环
    • 块中也存在保留环
    • 要点

前言

本章的内容是比较麻烦复杂的一章,牵扯到了GCD和Block,在之前也有学习过,本章作以深入的了解。多线程问题是开发应用程序的时候最让人头疼的问题,尤其是线程阻塞,在更新了Mac之后我经常的遇到了彩虹小球的问题,当然在开发的时候还没有出现过线程阻塞问题。

37.理解块的概念

块可以实现闭包,这个特性是作为扩展引入的,它也是基于C语言特性的技术,包括在C C++,OC,OC++代码使用它。

块的基础知识

块与函数类似,只不过是直接定义在另一个函数里的,和定义它的那个函数共享同一个范围内的东西。块用“^”符号来表示,后边跟着一对花括号,括号里面是块的实现代码。

^{
    block implementation 
}

块也是一个值,它的语法和函数类似,语法结构如下:

return_type (^block_name)(parameters)

具体实例,这是定义一个名字为addBlock的块变量,可以类似于函数去使用它

#import <Foundation/Foundation.h>
NSString* (^addBlock)(NSString *a, NSString* b) = ^(NSString *a, NSString* b) {
    return [NSString stringWithFormat:@"%@%@", a, b];
};
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSString* new = addBlock (@"3g", @"iOS");
        NSLog(@"new = %@", new);
    }
    return 0;
}

在这里插入图片描述

块可以捕获变量

块可以在它生命的范围里面所有变量都可以捕获。

#import <Foundation/Foundation.h>
NSString* c = @"ff";
NSString* (^addBlock)(NSString *a, NSString* b) = ^(NSString *a, NSString* b) {
    return [NSString stringWithFormat:@"%@%@%@", a, b, c];
};
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSString* new = addBlock (@"3g", @"iOS");
        NSLog(@"new = %@", new);
    }
    return 0;
}

在默认情况下我们捕获的C字符串是不可以修改的,不过声明变量的时候加入__block即可在块内修改。

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    __block NSString* c = @"ff";
    NSString* (^addBlock)(NSString *a, NSString* b) = ^(NSString *a, NSString* b) {
        c = @"1234";
        return [NSString stringWithFormat:@"%@%@%@", a, b, c];
    };
    @autoreleasepool {
        // insert code here...
        NSString* new = addBlock (@"3g", @"iOS");
        NSLog(@"new = %@", new);
    }
    return 0;
}

在这里插入图片描述

内联块的用法

如果块所捕获的变量是对象类型,那么就会自动保留它。系统在释放这个块的时候,也会将其一并释放。这就引出了一个于块有关的重要问题。块本身可视为对象。并且块本身也和其他对象一样,有引用计数。如果将块定义在OC类的实例方法中,那么除了可以访问类的所有实例变量之外,还可以使用self变量。块总能修改实例变量,所以在声明时无需加_block。不过,如果通过读取或写入操作捕获了实例变量,那么也会自动把self变量一并捕获了,因为实例变量是与self所指代的实例关联在一起的。也就是说,只要你在块中调用到了属性值,那么这个块就会捕获这个类本身也就是self。
在这里插入图片描述

块的内部结构

OC对象都会占用某个内存区域,块本身也是对象,也存在内存区域和内存布局。
在这里插入图片描述

  • 在存放块对象的内存区域中,首个变量是指向Class对象的指针,该指针叫做isa,最重要的就是invoke变量,这是个函数指针,指向块的实现代码。
  • 函数原型至少需要接受一个void*型的参数,此参数代表块,这其实就是一种代替函数指针的语法结构,把函数通过块封装成有用的接口。
  • descriptor变量是指向结构体的指针,每个块里都包含此结构体,其中声明里块对象的总体大小,还声明里copy与dispose这两个辅助函数所对应的函数指针。
  • 块还会把它所捕获的所有变量都拷贝一份。这些拷贝放在descriptor变量的后面,捕获了多少个变量,就要占据多少内存空间。拷贝的并不是对象本身,而是指向这些对象的指针变量。
  • invoke函数为何需要把块对象作为参数传进来呢?因为要从内存中把这些捕获到的变量读出来。

栈块

定义对象的时候是初始分配在栈上的,也就是有可能在使用之后内存被覆写,那样就是产生崩溃。
在这里插入图片描述

堆块

为了解决问题可以给块对象发送copy信息,这样子就会把块从栈复制到堆上,块也就成了带引用计数的对象了,在ARC下编译器会自动的合理的释放对象。
在这里插入图片描述

全局块

除了“栈块”和“堆块”之外,还有一类块叫做“全局块”。这种块不会捕捉任何状态(比如外围的变量),运行时也无须有状态来参与。而且全局块的copy属于空操作。可以把他认为是单例。

在使用单例模式封装网络请求的时候就是使用了全局块。

要点

  • 块是C、C++、OC中的词法闭包。
  • 块可接受参数,也可返回值。
  • 块可以分配在栈上或堆上,也可以是全局的。分配在栈上的块可拷贝到堆里,这样的话,就和标准的OC对象一样,具备引用计数了。

38.为常用的块类型创建typedef

单例模式封装网络请求的代码就用到了这一条。
为了隐藏复杂的块类型,需要用到C语言的类型定义,typedef关键字。

typedef int (^EOCSomeBlock)(BOOL flag, int value);

在这里插入图片描述
如此以来与定义其他变量时一样,变量类型在左边,变量名在右边。
块也可以用来简便方法签名。
在这里插入图片描述

要点

  • 以typedef重新定义块类型,可令块变量用起来更加简单。
  • 定义新类型时应遵从现有的命名习惯,勿使其名称与别的类型相冲突。
  • 不妨为同一个块签名定义多个类型别名。如果要重构的代码使用了块类型的某个别名,那么只需要修改相应typedef中的块签名即可,无须改动其他typedef。

39.用handler块降低代码分散程度

协议传值实现异步

为用户界面编码时,一种常用的范式就是“异步执行任务”。这种范式的好处在于:处理用户界面的显示及触摸操作所用的线程,不会因为要执行I/O或网络通信这类耗时的任务而阻塞。这个线程通常称为主线程。异步执行任务的通常使用委托模式实现,也就协议传值。

  • 写一个从URL中获取数据的嘞,使用委托模式设计
    在这里插入图片描述
  • 其他类像下面这样使用此类提供的API
    在这里插入图片描述
    这种做法可行,然而使用block块来写的话代码会更清晰。

块实现异步

将一个方法定义为块类型当作参数传给某个方法。
在这里插入图片描述
相比委托协议,块封装起来的时候可以在调用start方法时候以内联得形式定义completion handler,代码更加容易读懂。
委托模式还有缺点就是如果类分别使用多个获取器下载不同的数据,那么就得在delegate回调方法里根据传入参数切换。
在这里插入图片描述
使用块无需在回调方法里面切换,每个completion handler的逻辑都已经定义好了
在这里插入图片描述

回调操作里的块

有时需要在相关事件点执行回调操作,这种情况也可以使用handler块。就比如说是下载应用的进度条。我们为其添加一个观察者,并且在其值发生改变的时候我们调用其中的块。
在这里插入图片描述
此处传入的NSOperationQueue参数就表示触发通知时用来执行块代码的那个队列。这是个“队列操作”,而非“底层GCD队列” 这个在本章之后会学习到。

要点

  • 在创建对象时,可以使用内联的handler块将相关的业务逻辑一并声明。
  • 在有多个实例需要监控时,如果采用委托模式,那么经常需要根据传入的对象来切换,而若改用handler块来实现,则可以直接将块与相关对象放在一起。
  • 设计API时如果用到了handler块,那么可以增加一个参数,使用调用者可以通过此参数来决定应该把块安排在哪个队列上执行。

40.用块引用其所属对象时不要出现保留环

块中也存在保留环

如果块所捕获的对象直接或间接的保留了块本身,那么就会出现一种相互引用的现象吗,也就是块中的保留环。
在这里插入图片描述

  • 保留环主要还是互相引用,尤其是在块的回调部分出现,可能你意识不到的时候就存在了保留环。
  • 只要合适的时候清除掉一方引用,就可以解决问题。
    在这里插入图片描述

要点

  • 如果块所捕获的对象直接或间接的保留了块本身,那么就得当心保留环问题。
  • 一定要找个适当的时机解除保留环,而不能把责任推给API的调用者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1456647.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA高并发——JDK的并发容器

文章目录 1、超好用的工具类&#xff1a;并发集合简介2、线程安全的HashMap3、深入浅出ConcurrentHashMap3.1、ConcurrentHashMap的内部数据结构3.2、put()方法的实现3.3、get()方法的实现 4、有关List的线程安全5、高效读写的队列&#xff1a;深度剖析ConcurrentLinkedQueue类…

USB隔离模块(ADUM4160)--- 保护您的创新:从高精度到高电压

所需设备&#xff1a; 1、ISOUSB 隔离器&#xff1b; 数据信号、电源 双重隔离&#xff1b; ISOUSB 隔离器采用实现单个 USB 输入&#xff0c;单个USB 隔离输出功能&#xff0c;方便实际应用。另外&#xff0c;本产品采用 ADI 公司的 iCoupler 磁耦隔离技术&#xff0c;芯片…

震惊世界的Sora发明者之一,是毕业于上海交大的天才少年-谢赛宁?(本人辟谣)

很少发票圈&#xff0c;如果大家看到这个公众号标题党的离大谱的文章&#xff0c;求一定帮忙点下举报不实信息。如果有认识微信相关部门的朋友也请联系我一下。 Sora是bill他们在openai的呕心之作&#xff0c;我虽然不知道细节&#xff0c;但是bill告诉我他们每天基本不睡觉高强…

多线程——

目录 一、为什么要有多线程&#xff1f; 1、线程与进程 2、多线程的应用场景 3、小结​编辑 二、多线程中的两个概念&#xff08;并发和并行&#xff09; 1、并发 2、并行 3、小结 三、多线程的三种实现方式 1、继承Thread类的方式进行实现 2、实现Runnable接口的方…

【每日一题】三数之和

三数之和&#xff0c;点击左侧即可跳转。 目录 题目详情&#xff1a;思路&#xff1a;暴力&#xff1a;双指针&#xff1a; 代码实现&#xff1a; 题目详情&#xff1a; 思路&#xff1a; 暴力&#xff1a; 我们会显而易见的想到使用暴力解法&#xff0c;3个for循环暴力枚举…

如何在Ubuntu系统使用Docker部署开源白板工具Excalidraw并实现公网访问

文章目录 1. 安装Docker2. 使用Docker拉取Excalidraw镜像3. 创建并启动Excalidraw容器4. 本地连接测试5. 公网远程访问本地Excalidraw5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Ubuntu系统使用Docker部署开源白板工具Excal…

JavaAgent介绍 | 基本介绍及无侵入打印方法耗时

闲聊 最近在配置skywalking的过程中发现了-javaagent:这个配置&#xff0c;这里做一个简单的学习 什么是JavaAgent 网上似乎没有直接的文档介绍这个&#xff0c;只找了instrument包的相关文档&#xff0c;其内容页。其中的内容也都是介绍javaagent相关。 instrument包下核心…

【深蓝学院】移动机器人运动规划--第5章 最优轨迹生成--笔记

文章目录 1. Preliminaries2. Multicopter dynamics and differential flatness&#xff08;多旋翼动力学和微分平坦特性&#xff09;2.1 Differential Flatness2.2 具体建模2.3 Flatness Transformation的解析推导 3. Trajectory Optimization轨迹优化3.1 Problem formulation…

5年测试被裁,年前恶补3个月上岸字节28K,面试差点被问哭···

我的个人背景非常简单&#xff0c;也可以说丝毫没有亮点。 学历普通&#xff0c;计算机专业二本毕业&#xff0c;毕业后出来就一直在一家小公司&#xff0c;岁月如梭细&#xff0c;算了下至今从事软件测试已经5年了&#xff0c;也点点点了五年&#xff0c;每天都是重复的工作&…

【 JS 进阶 】原型对象、面向对象

目标 了解构造函数原型对象的语法特征&#xff0c;掌握 JavaScript 中面向对象编程的实现方式&#xff0c;基于面向对象编程思想实现 DOM 操作的封装。 了解面向对象编程的一般特征掌握基于构造函数原型对象的逻辑封装掌握基于原型对象实现的继承理解何为原型链及其作用能够处理…

安全测试工具安装指南:在统信UOS上部署Burp Suite

原文链接&#xff1a;安全测试工具安装指南&#xff1a;在统信UOS上部署Burp Suite 大家好&#xff01;在网络安全领域&#xff0c;Burp Suite是一款不可或缺的工具&#xff0c;它提供了从初级映射和分析应用程序攻击面到查找和利用安全漏洞的一系列功能。今天&#xff0c;我将…

Pytorch框架-----torch.tensor(创建张量)

文章目录 前言一、torch.Tensor二、构建tensor1.从Python的list或序列构建2.空张量3.索引和切片来获取和修改一个张量tensor中的内容 前言 torch.Tensor 是包的核心类。如果将其属性 .requires_grad 设置为 True&#xff0c;则会开始跟踪针对 tensor的所有操作。完成计算后&am…

VS引用第三方库

使用Qt开发习惯了&#xff0c;切换来VS环境&#xff0c;居然引用第三方库&#xff0c;都有所不适应&#xff1b;因为之前都是在Qt项目的pro文件里面直接手写配置好第三方库的include目录、lib目录和依赖的lib名称和拷贝dll语句. Vs也一样&#xff0c;如下三步&#xff1a; 第一…

世界顶级名校计算机专业,都在用哪些书当教材?(文末送书)

目录 01《深入理解计算机系统》02《算法导论》03《计算机程序的构造和解释》04《数据库系统概念》05《计算机组成与设计&#xff1a;硬件/软件接口》06《离散数学及其应用》07《组合数学》08《斯坦福算法博弈论二十讲》参与规则 清华、北大、MIT、CMU、斯坦福的学霸们在新学期里…

板块一 Servlet编程:第三节 HttpServletRequest对象全解与请求转发 来自【汤米尼克的JAVAEE全套教程专栏】

板块一 Servlet编程&#xff1a;第三节 HttpServletRequest对象全解与请求转发 一、什么是HttpServletRequest二、接收请求的常用方法三、请求乱码问题四、请求转发&#xff1a;forward五、Request作用域getParameter和getAttribute的区别 在上一节中我们已经学习了完整的Servl…

【Linux】进程地址空间的理解

进程地址空间的理解 一&#xff0c;什么是程序地址空间二&#xff0c;页表和虚拟地址空间三&#xff0c;为什么要有进程地址空间 一&#xff0c;什么是程序地址空间 在我们写程序时&#xff0c;都会有这样下面的内存结构&#xff0c;来存放变量和代码等数据。 一个进程要执行…

【安全狐】Windows隐藏计划任务技术及排查方法

0x00 前置知识 计划任务SCHTASKS命令 SCHTASKSSCHTASKS /Create 参数 SCHTASKS /Create [/S system [/U username [/P [password]]]][/RU username [/RP password]] /SC schedule [/MO modifier] [/D day][/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttim…

掘根宝典之C++深复制与浅复制(复制构造函数,默认复制构造函数)

到目前为止我们已经学了构造函数&#xff0c;默认构造函数&#xff0c;析构函数&#xff1a;http://t.csdnimg.cn/EOQxx 转换函数&#xff0c;转换构造函数&#xff1a;http://t.csdnimg.cn/kiHo6 友元函数&#xff1a;http://t.csdnimg.cn/To8Tj 接下来我们来学习一个新函数…

FL Studio21.2.3最新版一键安装版专业版水果FLStudio 2024最新下载

FL Studio21.2.3.4004中文破解版系统要求&#xff1a; 版本&#xff1a;v21.2.3 内部版本 [4004] 开发商&#xff1a; Image-Line 格式&#xff1a;独立、VST 位深度&#xff1a;64位 界面语言&#xff1a;英语、德语、西班牙语、法语、中文。 系统环境 Microsoft Window…

AcuAutomate:一款基于Acunetix的大规模自动化渗透测试与漏洞扫描工具

关于AcuAutomate AcuAutomate是一款基于Acunetix的大规模自动化渗透测试与漏洞扫描工具&#xff0c;该工具旨在辅助研究人员执行大规模的渗透测试任务。 在大规模的安全测试活动中&#xff0c;AcuAutomate可以帮助我们同时启动或停止多个Acunetix扫描任务。除此之外&#xff…