没有(NS)FileManager的世界
您能想象没有标准库的现代编程语言吗? 这种香草技术不是很有用。 每当您需要字符串操作或链表时,您都必须重新发明轮子。 很难想象,但是为了学习,也许您应该这样做。
从人到人
一遍又一遍地编写相同的功能与通用方法是矛盾的,后者站在巨人的肩膀上,采用经过战斗验证的代码并以此为基础。 标准库不是全能者从天上赐给我们的神圣礼物 。
这只是人们为人们编写的代码,因此我们可以对其进行调查并从中学习。
Swift编程语言具有可靠的标准库。 您可以在其中找到大量协议和数据结构,这对我们的日常工作至关重要。 它并不止于此。
如果有Swift,则必须有标准库,但是您也可以选择使用Foundation框架。 该框架是Objective-C的遗产,可为我们提供对操作系统服务(及更多)的访问。 作为iOS开发人员,无论您是否想要, Foundation都是您工作中必不可少的工具 。
一个众所周知的事实是,当您从事物中获取事物时,您会更加欣赏它们的价值。 这是一项很棒的运动,可以帮助您成长。
让我们在实践中尝试一下。 想象一下,您需要加载一个文本文件并将其内容打印到控制台。 您有一个空旷的操场,不能使用Foundation。 我们什至从哪里开始?
游乐场沙箱
我们需要解决的第一个重要主题是创建要读取的文件。 由于Playground的沙箱,访问文件并非易事,它只允许我们访问特定目录中的文件。
使用Foundation,这很容易,但是我们只是摆脱了使用它的特权。 存放文件的最基本的位置是游乐场束的Resources目录中。 要访问它,您可以使用:
Bundle.main.path(forResource:“文件”,ofType:“ txt”)
没有Foundation,将很难生成您将获得的URL。
/var/folders/p6/bzwgcq8j5gbg60b_lqprskch0000gp/T/com.apple.dt.Xcode.pg/resources/0A333528–48D8–4FF7–885E-9E2151867350/file.txt
我们可以访问的另一个位置是“ 共享游乐场数据”文件夹,我们可以在“文档”中创建此文件夹。 您可以使用PlaygroundSharedDataDirectory全局变量(PlaygroundSupport模块)获取路径,但是它包含URL(Foundation结构!),因此我们将在此练习中使用硬编码的路径(将来可能无法使用)。
通过在终端中键入以下命令来准备测试文件(-e in echo是用于启用反斜杠转义解释的标志):
mkdir〜/ Documents / Shared \ Playground \ Data
echo -e“要打印的示例文本。\ n新行文本。”>〜/ Documents / Shared \ Playground \ Data / file.txt
现在,我们可以使用保存文件路径的常量开始编码:
开源的力量
自Swift开源以来,我们的任务就变得容易得多。 我们可以窥视一下并决定要做什么。 Foundation的存储库是一本可供检查的公开书籍。 如果要使用FileManager,将使用以下命令加载文件:
注意:我的链接指向swift 3分支(而不是master)上的最后一次提交,以在将来保留行号兼容性。
让我们在这里看一下FileManager的实现:
评论很清楚。 下一站—数据(contentsOf:选项:)
您可以看到这里发生了一些NSData兼容性魔术,但是现在对我们来说唯一有趣的是转发到NSData的初始化。
我们知道我们的路径指向文件,因此我们转到另一个init方法:
最后,我们在最后一站找到了所需的代码。 我们的旅程的Stacktrace看起来像这样:
这是readBytesFromFileWithExtendedAttributres的主体:
达尔文
快速检查readBytesFromFileWithExtendedAttributes(_:options :)之后,我们可以看到几个奇怪的免费函数(不属于任何类),例如stat , 关闭并在整个方法上读取 。
他们来自哪里? 文件顶部的导入可以启发我们。 它们来自Darwin(如果您使用Linux,则为Glibc)模块。 达尔文(Darwin)是由Apple开发的开源操作系统,为macOS提供核心组件。
Darwin模块只是一堆标题,可为我们提供有关操作系统服务的指导。 尽我们所能,因为这些服务是桥接到Swift的C函数。 接下来,我们将使用的大多数内容都是标准C库的一部分,因此,如果您愿意的话,可以看一看实现libc的存储库。 多亏了POSIX(一套用于Unix操作系统系列之间兼容性的标准),所有方法都具有相同的接口,因此可以轻松地导航到那里。
不安全指针
对于Swift开发人员来说,使用C方法通常会感到很奇怪,但这是我们访问大量C标准库功能所需付出的代价。 经常遇到的问题是以UnsafePointer的形式处理内存块。
如果您不知道指针是什么,或者第一次看到UnsafePointer类,则应先从下面的链接中阅读资源,然后继续前进:
https://zh.wikipedia.org/wiki/指针_(计算机编程)
https://developer.apple.com/reference/swift/unsafepointer
文件大小
好,该写代码了。 让我们开始创建一个函数,该函数将为我们保存所有代码。 将指令放在函数中(而不是全局范围)会很有用,因为我们可以使用保护而不是多层ifs。
现在在printContent(atPath :)内部,我们需要获取文件大小才能继续。
stat(来自Darwin> POSIX> sys> stat)采用UnsafePointer 形式的路径-以\ 0结尾的字符列表。 幸运的是,Swift确实在没有我们帮助的情况下从String过渡了。 第二个参数是指向inout结构的指针,该结构将包含有关文件的所有信息。 返回值为Int32 。 如果操作成功,它包含0,否则包含-1。
检查错误很重要,这样我们才能用if保护自己。 上次操作的错误代码存储在errno全局变量中。 我们使用strerror(Darwin> C> String)来打印与代码有关的消息。 我们将所有这些封装在printContent(atPath 🙂之外的一个简单函数中
然后,回到主流中,我们可以添加以下内容:
如果我们将路径中的文件名更改为不存在的名称,则应该在Playground的“调试区域”中收到一条消息:
错误代码2:没有这样的文件或目录。
开启档案
要实际打开文件,我们将使用open,它带有一个路径和选项(以Int32标志的形式组合在一起成为一个值)。 当从文件中读取文件时,它返回文件描述符,该文件将是我们的句柄的数字ID。 完成后,我们还需要关闭连接。 当我们离开printContent(atPath 🙂的范围时,我们可以使用defer自动执行此操作
读取文件
现在最有趣的部分是从文件读取。 首先,我们需要为内容创建一个缓冲区。 malloc命令操作系统以给定大小在堆上分配内存,并返回指向该内存的指针。 不幸的是,我们收到的指针不是由ARC管理的,因此我们必须免费手动释放它。
要获取文件的内容,我们使用read。 读取过程可能需要多次迭代,因此我们需要一些临时变量来记录进度。 如果在某个时候读数返回-1,则表明存在错误,因此我们完成了该操作:
在运行我们已经拥有的代码之后,您将在右侧面板中看到我们的小文件仅需要一次迭代即可完成操作。
打印
除了打印文件内容外,别无其他。 不幸的是,我们拥有的数据不是以空值结尾的(这是String(cString :)知道如何读取数据的方式),因此我们需要在获取String之前进行一些处理。
我们需要将UnsafeMutablePointerRaw更改为UnsafePointer (碰巧,我们知道我们的文件是UTF8,每8位(字节)存储一个字符)。 然后,我们需要将整数映射到可以轻松转换为String的字符数组。 最后一步是打印文件的内容。
一起使用的所有代码如下所示:
摘要
好奇心对于软件开发人员来说是一个非常重要的特征 。
好奇心使我们问:
如果没有FileManager,世界将如何?
总之,我们不仅知道如何在不使用Foundation的情况下读取文件,而且还知道如何使用Swift中的C库。 这打开了一个全新的可能性范围。 现在,我们可以使用已知有用的旧库,因为它们已经存在了一段时间。 最后,好奇心使您进入了这篇技术性文章的结尾,所以谢谢。 你真棒!
您可以通过此链接在文章中找到完成的示例。
请随时在下面的评论中或通过社交媒体与我们分享您的经验 (也向我们发送一些照片或视频),您可以在Facebook,Twitter,Instagram和Pinterest上找到我们,让我们联系!
要了解有关App’n’roll的更多信息,请查看我们的网站和其他文章。 如果您喜欢阅读本文,请点击鼓掌并推荐!
使用的所有图像均为 CC0 1.0 Universal(CC0 1.0) 。