【iOS】系统框架

文章目录

  • 前言
  • 四十七、熟悉系统框架
  • 四十八、多用块枚举,少用for循环
  • 四十九、对自定义其内存管理语义的collection使用无缝桥接
  • 五十、构建缓存时选用NSCache而非NSDictionary
  • 五十一、精简initialize与load的实现代码
  • 五十二、别忘了NSTimer会保留其目标对象


前言

本次博客撰写小蓝书的最后一章系统框架

四十七、熟悉系统框架

OC的Foundation框架,像NSObject NSArray, NSDictionary等类都在其中。Foundation框架里的类都是用NS前缀,因为OC之前作为NeXTSTEP操作系统确定的。

将一系列代码封装为动态库,并在其中放入描述其接口的头文件,这样做出来的东西就叫框架。

CoreFoundation框架

Foundation框架提供了collection等基础核心功能,而且还提供了字符串处理等复杂功能。还存在一个CoreFoundation框架,在之前了解过他是不属于OC框架之内的,但是OC应用程序的编写离不开这个框架,Foundation框架的许多功在CoreFoundation框架都可以找到对应的C语言API

他其中的很多类都和Foundation框架相似,并且我们还可以通过“无缝桥接”功能实现CoreFoundation框架中的C语言数据结构平滑转换为Foundation框架中的OC对象,也可以反向转换。无缝桥接技术是用某些相当复杂的代码实现出来的,这些代码可以使运行期系统把CoreFoundation框架中的对象视为普通的OC对象。

NSString所对应的就是CFString对象。

其他框架:
在这里插入图片描述
OC编程的重要特点就是:经常需要使用底层的C语言级 API。用c语言实现 API的好处是,可以统过 Objeotive-C 的运行期系统,从而提升执行速度

要点

  • 许多系统框架都可以直接使用。其中最重要的是Foundation与CoreFoundation,这两个框架提供了构建应用程序所需的许多核心功能。
  • 很多常见任务都能用框架来做,例如音频与视频处理、网络通信、数据管理等。
  • 请记住:用纯C写成的框架与用OC写成的一样重要,若想成为优秀的OC开发者,应该掌握C语言的核心概念。

四十八、多用块枚举,少用for循环

语言中引人“块” 这一特性后,又多出来儿种新的通历方 式,而这几种方式容易为开发者所忽视。采用这几种新方式遍历collection 时,可以传人块, 而collection 中的每个元素都可能会放在块里运行一遍,这种做法通常会大幅度简化编码过程以下进行各种遍历方法详细说明:

for循环
在这里插入图片描述
根据定义,因为字典与set对象是无需的,所以无法通过下标访问,所以需要先获取字典里的所有键值或是set中的所有对象,这两种情况下都可以放到数组中实现,就如上述代码所显示

NSEnumerator遍历
NSEnumerator是个抽象基类,其中只定义了两个方法,供其具体子类来实现:
在这里插入图片描述
关键的是其中的nextObject对象,它返回枚举里的下个对象,当返回不为nil的时候就会一直调用下一个对象,常用while语句

对于set与字典写法其实也与标准的for循环相似,只是代码多了一些,其优势在于无论遍历哪一种collection,都可以采用这套相似的语法
在这里插入图片描述

快速遍历
快速遍历是OC2.0引入的语法功能,引入了in关键字,语法更加简洁了collection的遍历过程。尤其是字典类
在这里插入图片描述

基于块的遍历方式
在这里插入图片描述
参数1是每次枚举的对象, idx是下标, stop则是代表是否停止遍历

使用这种方法遍历时既能获取对象,也能知道其下标

    [t enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@ %lu", obj, (unsigned long)idx);      
    }];

对于字典,还可获得其key与object
在这里插入图片描述

此方式大大胜; 过其他 方式的地方在于:遍历时可以直接从块里获取更多信息。在遍历数 组时,可以知道当前所针对的下标。遍历有序set (
NSOrderedset )时也一样。而在遍历字典 时, 无须额外编码,即可同时获取键与值,因而省去了根据给定键来获取对应值这一步。用
这种方式遍历字典,可以同时得知键与值,这很可能比其他方式快很多,因为在字典内部的 数据结构中,键与值本来就是存储在一起的。

在这里插入图片描述
同时还可通过选项掩码进行反向遍历

要点

  • 遍历collection有四种方式。最基本的办法是for循环,其次是NSEnumerator遍历法及快速遍历法,最新、最先进的方式则是“块枚举法”。
  • “块枚举法”本身就能通过GCD来并发执行遍历操作,无须另行编写代码。而采用其他遍历方式则无法轻易实现这一点。
  • 若提前知道待遍历的collection含有何种对象,则应修改块签名,指出对象的具体类型

四十九、对自定义其内存管理语义的collection使用无缝桥接

Objective-C的系统库包含相当多的collection 类,其中有各种数组、各种字典、各种 setoFoundation框架定义了这些collection及其他各种collection所对应的Objective-C类 。与 之相似,CoreFoundation 框架也定义了一套C语言API,用于操作表示这些collection 及其他 各种collection 的数据结构。

例如,NSAray 是Foundation 框架中表示数组的Objective-C类, 而CFArray 则是CoreFoundation 框架中的等价物。这两种创建数组的方式也许有区别,然而 有 项 强 大 的 功 能 可 在 这 两 个 类 型 之 间 平 滑 转 换, 它 就 是 “ 无 缝 桥 接” ( t o l l - f r e e b r i d g i n g ) 。

转换操作中的__bridge告诉ARC如何处理转换所涉及的OC对象。__bridge本身的意思是:ARC仍然具备这个OC对象的所有权。而__bridge_retained则与之相反,意味着ARC将交出对象的所有权。与之相似,反向转换可通过__bridge_transfer来实现,也就是将对象的所有权交给ARC。这三种转换方式称为“桥式转换”

- (void)seamlessBridging {
    NSArray *testArray = @[@"111", @"222", @"333",];
    CFArrayRef aCFArray = (__bridge  CFArrayRef)testArray;
    NSLog(@"cfArratSize =  %li", CFArrayGetCount(aCFArray));
}

要点

  • 通过无缝桥接技术,可以在Foundation框架中的OC对象与CoreFoundation框架中的C语言数据结构之间来回转换。
  • 在CoreFoundation层面创建collection时,可以指定许多回调函数,这些函数表示此collection应如何处理其元素。然后,可运用无缝桥接技术,将其转换成具备特殊内存管理语义的OC collection。

五十、构建缓存时选用NSCache而非NSDictionary

开 发 M a c O s × 或 i 0 s 应 用 程 序 时 , 经 常 会 遇 到 一个 问 题 , 那 就 是 从 因 特 网 下 载 的 图 片 应如何来缓存。首先能想到的好办法就是把内存中的图片保存到字典里,这样的话,稍后使 用 时 就 无 须 再 次 下 载 了。 有 些 程 序 员 会 不 假 思 索 , 直 接 使 用 N S D i c t i o n a r y 来 做 (准 确 来 说 , 是使用其可变版本),因为这个类很常用。其实,NSCache 类更好,它是Foundation 框架专 为处理这种任务而设计的。

NSCache 胜过NSDictionary 之处在于,当系统资源將要耗尽时,它可以自动删减缓存
另外,NSCache 是线程安全的。 而NSDictionary 则绝对不具备此优势

要点

  • 实现缓存时应选用NSCache而非NSDictionary对象。因为NSCache可以提供优雅的自动删减功能,而且是“线程安全的”,此外,它与字典不同,并不会拷贝键。
  • 可以给NSCache对象设置上限,用以限制缓存中的对象总个数及“总成本”,而这些尺度则定义了缓存删减其中对象的时机。但是绝对不要把这些尺度当成可靠的“硬限制”,它们仅对NSCache起指导作用。
  • 将NSPurgeableData与NSCache搭配使用,可实现自动清除数据的功能,也就是说,当NSPurgeableData对象所内存为系统所丢弃时,该对象自身也会从缓存中移除。
  • 如果缓存使用得当,那么应用程序的响应速度就能提高。只有那种“重新计算起来很费事的”数据,才值得放入缓存,比如那些需要从网络获取或从磁盘读取的数据。

五十一、精简initialize与load的实现代码

在OC里一个类必须初始化才能使用,大多数类继承与NSObject这个根类,提供了loadinitalize方法

load
加入运行期系统中的每个类和分类来说,会调用此方且仅调用一次,当类和分类的程序载入系统的时候就会执行这个方法,调用顺序是类大于分类

总结一句话就是不要用load

initalize
该方法是在程序首次使用该类之前调用且仅有一次,是由运行期系统调用的,不通过代码调用。和load存在区别

  • 惰性调用当程序用到了相关类的时候才会调用,类似于懒加载模式。而load是所有类不管用不用先load方法之后再说
  • 其次运行期在执行该方法的时候是系统正常状态,也就是安全状态,不影响调用类的其他方法,此为线程安全。
  • 当某个类没实现initialize方法,超类实现后会调用超类的方法,和大多数消息是一样的。

initalize方法尽量精简

  • 首先,大家都不想看到自己的应用程序“挂起”,若写的太过繁琐,导致其运行很慢那就适得其反了。
  • 开发者无法控制类的初始化时机。
  • 最后,如果某个类的实现代码很复杂,那么其中可能会直接或间接用到其他类。若那些类尚未初始化,则系统会迫使其初始化。然而,本类的初始化方法此时尚未运行完毕。其他类在运行其initialize方法时,有可能会依赖本类中的某些数据,而这些数据此时也许还未初始化好,就会造成依赖环

initalize方法只应该用来设置内部数据,不能在内部调用其他的方法。

initalize还可以初始化某个无法在编译器初始化的全局变量
在这里插入图片描述
但别忘了单例类也可以实现该目的

要点

  • 在加载阶段,如果类实现了load方法,那么系统就会调用它。分类里也可以定义此方法,类的load方法要比分类中的先调用。与其他方法不同,load方法不参与覆写机制。
  • 首次使用某个类之前,系统会向其发送initialize消息。由于此方法遵从普通的覆写规则,所以通常应该在里面判断当前要初始化的是那个类。
  • load与initialize方法都应该实现的精简一些,这有助于保持应用程序的响应能力,也能减少引入“依赖环”的几率。
  • 无法在编译期设定的全局常量,可以放在initialize方法里初始化。

五十二、别忘了NSTimer会保留其目标对象

Foundation框架中有个类叫NSTimer,开发者可以指定绝对的日期与时间,以便到时执行任务,计时器要和“运行循环(run loop)”相关联,运行循环到时候会触发任务。创建NSTimer时,可以将其“预先安排”在当前的运行循环中,也可以先创建好,然后由开发者自己来调度。无论采用哪种方式,只有把计时器放在运行环里,它才能正常触发任务

创建计时器

 _pollTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(pDoPoll) userInfo:nil repeats:YES];

保留环

创建计时器的时候,由于目标对象是self,所以要保留此实例。然而,因为计时器是用实例变量存放的,所以实例也保留了计时器,于是,就产生了保留环。所以说,调用stopPolling,或者令系统将此实例回收,只有这样才能打破保留环。

因为是类和这个类中的实例出现了保留环,不管你外界怎么对这个类释放,这个计时器始终都会保留这个类,而这个类也会保留这个计时器,互相引用保留导致他们的计数永远都不会降为0

在这里插入图片描述
如果从外界直接先调用stop方法,代码没办法自己检测。

使用块的特点打破保留环

使用块和weak关键字合理的打破保留环,块可以传递代码,这一功能可以利用
在这里插入图片描述
在这里插入图片描述
这个办法为何能解快“ 保留环〞问题呢?大家马上就会明白。这段代码将计时器所应执 行的任务封装成“ 块”,在调用计时器西数时,把它作为userlnfo 参数传进去。该参数可用来 存 放 “ 不 透 明 值 ” ( o p a q u e v a l u e )。 , 只 要 计 时 器 还 有 效 , 就 会 一直 保 留 着 它 。 传 人 参 数 时 要 通 过copy方法将block拷贝到“堆” 上(之前在blk提过,copy方法把块变成了有引用计数的对象。),否则等到稍后要执行它的时候,该块 可能己经无效了。计时器现在的target 是NSTimer 类对象,这是个单例,因此计时器是否会 保 留 它, 其 实 都 无 所 谓 。 此 处 依 然 有 保 留 环 , 然 而 因 为 类 对 象 ( c l a s s o b j e c t ) 无 须 回 收 , 所 以 不用担心。

此处依然有保留环,使用weak关键字打破它
在这里插入图片描述
这 段 代 码 采 用 了一 种 很 有 效 的 写 法 , 它 先 定 义 了一 个 弱 引 用 , 令 其 指 向 s e l f , 然 后 使 块 捕获这个引用,而不直接去捕获普通的sel f 变量。也就是说,sel f 不会为计时器所保留。当 块开始执行时,立刻生成strong 引用,以保证实例在执行期间特续存活。

采用这种写法之后,如果外界指向EOCClass实例的最后 一个引用将其释放,则该实例 就 可 为 系 统 所 回 收 了。

要点

  • NSTimer对象会保留其目标,直到计时器本身失效为止,调用invalidate方法可令计时器失效,另外,一次性的计时器在触发完成任务之后也会失效。
  • 反复执行任务的计时器,很容易引人保留环,如果这种计时器的目标对象又保留了计时器本身,那肯定会导致保留环。这种环状保留关系,可能是直接发生的,也可能是通过对象图里的其他对象间接发生的。
  • 可以扩充NSTimer的功能,用“块” 来打破保留环。不过,除非NSTimer将来在公共接口里提供此功能,否则必须创建分类,将相关实现代码加入其中。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+Dubbo构建的电商平台-微服务架构、商城、电商、微服务、高并发、kafka、Elasticsearc+源代码+文档说明

文章目录 项目用到的技术前端使用的技术后端使用的技术项目模块说明项目搭建方式项目开发进度源码下载地址 项目基于springboot2.1.6.RELEASEDubbo2.7.3 来构建微服务。 业务模块划分,尽量贴合互联网公司的架构体系。所以,除了业务本身的复杂度不是很高之…

《Solidity 简易速速上手小册》第4章:智能合约的设计与开发(2024 最新版)

文章目录 4.1 合约结构和布局4.1.1 基础知识解析深入合约布局原则理解组织结构高效布局的重要性 4.1.2 重点案例:构建一个在线商店合约案例 Demo:编写在线商店智能合约案例代码:OnlineStore.sol测试和验证拓展功能 4.1.3 拓展案例 1&#xff…

Django学习笔记-创建第一个django项目

1.创建一个虚拟环境的python项目 2.点击解释器设置 3.安装django包 4.终端选择Command Prompt 5.创建django项目运行django-admin startproject demo01(自命名) 6.修改连接数据库为mysql 7.修改语言(中国汉语)和时区(亚洲上海) 8.修改TEMPLATES 9.创建templates文件夹 10.安…

二、双指针问题

283、移动零(简单) 题目描述 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12]…

深度学习发展的艺术

将人类直觉和相关数学见解结合后,经过大量研究试错后的结晶,产生了一些成功的深度学习模型。 深度学习模型的进展是理论研究与实践经验相结合的产物。科学家和工程师们借鉴了人类大脑神经元工作原理的基本直觉,并将这种生物学灵感转化为数学模…

git pull CONFLICT 哪些是本地内容,哪些是远端仓库内容?

如上图&#xff0c;<<<<<< HEAD 是本地内容&#xff0c;>>>>>>> <remote_branch> 是远端仓库内容

HBase 进阶

参考来源: B站尚硅谷HBase2.x 目录 Master 架构RegionServer 架构写流程MemStore Flush读流程HFile 结构读流程合并读取数据优化 StoreFile CompactionRegion Split预分区&#xff08;自定义分区&#xff09;系统拆分 Master 架构 Master详细架构 1&#xff09;Meta 表格介…

设计模式之委派模式

文章目录 前言正文一、生活中的例子二、Java代码实现2.1 类设计2.2 代码实现2.2.1 Employee2.2.2 ArchitectureDesignEmployer2.2.3 BackEmployer2.2.4 FrontEmployer2.2.5 Leader2.2.6 EmployeeStrongPointEnum2.2.7 Boss 2.3 测试2.3.1 Client2.3.2 测试结果 三、委派模式的优…

SQL Developer 小贴士:显示RAC配置

前提&#xff1a; 已建立2节点RAC已在SQL Developer中建立了2个连接&#xff0c;分别到RAC的两个节点 然后单击菜单View>DBA&#xff0c;分别连接RAC节点1和节点2&#xff0c;并组织成目录&#xff08;不必须&#xff0c;但建议&#xff09;。 在两处可以体现为RAC配置。第…

Keepalived实现Nginx的高可用集群案例

服务器规划: serverb(nginx2):192.168.233.144 serverc(客户端):192.168.233.140 serverd(nginx1):192.168.233.141 结构图: serverd(nginx1): # 安装nginx yum install nginx -y# 进入nginx配置目录 cd /e…

【安全狐】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…

【MATLAB源码-第141期】基于matlab的免疫优化算法在物流配送中心选址应用仿真,输出选址图以及算法适应度曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 免疫优化算法在物流配送中心选址中的应用是一个集成了信息科学、生物学原理和运筹学的跨学科研究领域。本文旨在探讨免疫优化算法在物流配送中心选址问题中的应用&#xff0c;包括算法的基本原理、模型构建、算法实现及其在实…

华为配置旁挂二层组网隧道转发示例

配置旁挂二层组网隧道转发示例 组网图形 业务需求组网需求数据规划配置思路配置注意事项操作步骤配置文件扩展阅读 业务需求 企业用户通过WLAN接入网络&#xff0c;以满足移动办公的最基本需求。且在覆盖区域内移动发生漫游时&#xff0c;不影响用户的业务使用。 组网需求 AC组…

GPIO控制和命名规则

Linux提供了GPIO子系统驱动框架&#xff0c;使用该驱动框架即可灵活地控制板子上的GPIO。 GPIO命名 泰山派开发板板载了一个40PIN 2.54间距的贴片排针&#xff0c;排针的引脚定义兼容经典40PIN接口。 在后续对GPIO进行操作前&#xff0c;我们需要先了解k3566的GPIO命名规则&a…

Sublime替换文本中的换行/回车符等特殊符号

1、快捷键打开查找替换&#xff08;windows&#xff09; Ctrl h 2、开启打开查找窗口最左侧的(.*)正则匹配功能&#xff0c;上图中箭头所指。 3、Find栏输出被替换的正则表达式&#xff0c;如\n 回车符&#xff0c;表达式会有颜色显示 4、Replace栏输入替换后的内容&#xff0…

第8章 对同步的硬件支持

为了保证并行程序执行的正确性和高效性&#xff0c;构建一个共享存储多处理器系统的硬件支持必须要解决缓存一致性、存储一致性和对同步原语的支持等问题。从软件的观点来看被广泛使用的同步原语包括锁、栅栏和点对点同步&#xff08;信号量&#xff09;。举例来说&#xff0c;…

用于将Grafana默认数据库sqlite3迁移到MySQL数据库

以下是一个方案&#xff0c;用于将Grafana数据迁移到MySQL数据库。 背景: grafana 默认采用的是sqlite3&#xff0c;当我们要以集群形式部署的时使用mysql较为方便&#xff0c;试了很多sqlite转mysql的方法要么收费,最后放弃。选择自己动手风衣足食。 目标: 迁移sqlite3切换…

Vue报错,xxx is defined #变量未定义

vue.js:5129 [Vue warn]: Error in v-on handler: "ReferenceError: count is not defined" 浏览器将这个变量 当做全局变量了&#xff0c;事实上它只是实例中的变量 加上this指定&#xff0c;是vue实例中的变量

进程链信任-父进程欺骗

文章目录 前记普通权限的父进程欺骗ShllCode上线进程提权基础进程提权注入 前记 父进程欺骗作用&#xff1a; 进程链信任免杀进程提权 检测&#xff1a; etw 普通权限的父进程欺骗 #include<stdio.h> #include<windows.h> #include <TlHelp32.h>DWORD …

跳过测试方法(测试类)(@Ignore)

1.什么情况下要使用跳过测试(测试类)方法? 写了一个测试方法但是不想执行 删掉该测试方法&#xff08;测试类&#xff09;注释该测试方法&#xff08;测试类&#xff09;使用Ignore注解 2.示例 2.1 必要工作 导入类库 import org.junit.Ignore; 2.2 使用Ignore注解跳过…