没有(NS)FileManager的世界

您能想象没有标准库的现代编程语言吗? 这种香草技术不是很有用。 每当您需要字符串操作或链表时,您都必须重新发明轮子。 很难想象,但是为了学习,也许您应该这样做。

从人到人

一遍又一遍地编写相同的功能与通用方法是矛盾的,后者站在巨人的肩膀上,采用经过战斗验证的代码并以此为基础。 标准库不是全能者从天上赐给我们的神圣礼物

这只是人们为人们编写的代码,因此我们可以对其进行调查并从中学习。

Swift编程语言具有可靠的标准库。 您可以在其中找到大量协议和数据结构,这对我们的日常工作至关重要。 它并不止于此。

如果有Swift,则必须有标准库,但是您也可以选择使用Foundation框架。 该框架是Objective-C的遗产,可为我们提供对操作系统服务(及更多)的访问。 作为iOS开发人员,无论您是否想要, Foundation都是您工作中必不可少的工具

一个众所周知的事实是,当您从事物中获取事物时,您会更加欣赏它们的价值。 这是一项很棒的运动,可以帮助您成长。

让我们在实践中尝试一下。 想象一下,您需要加载一个文本文件并将其内容打印到控制台。 您有一个空旷的操场,不能使用Foundation。 我们什至从哪里开始?

游乐场沙箱

我们需要解决的第一个重要主题是创建要读取的文件。 由于Playground的沙箱,访问文件并非易事,它只允许我们访问特定目录中的文件。

在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)

Interesting Posts