EMFileStream 基于 stdio 的 Swift 文件流操作库

bmdb5781 7年前
   <p>这是一款基于Swift3.0的文件流操作库</p>    <h2>引言</h2>    <p>由于项目原因,将一些用 C++ 实现的库移植到了 iOS 中。移植过程必然造了不少轮子,本文将开源一个基于 stdio 的 Swift 文件流操作库。底层由 C语言 接口实现,可以简化 Swift 对文件流的操作过程。</p>    <p>这个库不仅适用于 Swift 开发的 iOS App ,也同样适用于 Swift 开发的 macOS 和 Linux 程序。</p>    <h2>实现</h2>    <p>由于底层功能由 C语言 实现,文件读写都依赖缓冲区实现,所以就会用到大量指针操作,这对 Swift 来说简直就是灾难。因此我造了一些轮子来避免指针操作以及使ARC也能管理这些内存。</p>    <h2>EMMemory</h2>    <p>该工具类将接管所有游离的内存缓冲区,封装过后使缓冲区拥有一定的C指针功能,同时使ARC得以维护这段内存。</p>    <p>作为ARC的媒介封装,在构造函数中分配内存,在析构函数中释放内存,使得C分配的缓冲区内存得以管理:</p>    <pre>  <code class="language-swift">open class EMMemory {         openvar mptr: UnsafeMutablePointer         openvar size: Int = 0         public init(size: Int) {          self.mptr = UnsafeMutablePointer.allocate(capacity: size)          self.size = size      }         deinit {          mptr.deallocate(capacity: size)      }     }  </code></pre>    <p>添加下标使其获得C语言指针的能力:</p>    <pre>  <code class="language-swift">    opensubscript(index: Int) -> UnsafeMutablePointer {          return mptr.advanced(by: index)      }  </code></pre>    <p>最后完善Swift不可变指针属性和数据数组属性:</p>    <pre>  <code class="language-swift">    openvar iptr: UnsafePointer {          get {              return UnsafePointer.init(mptr)          }      }         openvar data: Array {          get {              var data = Array.init(repeating: 0, count: size)              for i in 0..  </code></pre>    <p>有了这个类以后,我们不再使用 allocate 分配内存区块,由创建该对象接管。</p>    <h2>stdio文件操作</h2>    <p>stdio 中对于文件的操作函数有很多,本框架主要用到如下几个函数:</p>    <pre>  <code class="language-swift">public funcfopen(_ __filename: UnsafePointer!, _ __mode: UnsafePointer!) -> UnsafeMutablePointer!     public funcfclose(_: UnsafeMutablePointer!) -> Int32     public funcfread(_ __ptr: UnsafeMutableRawPointer!, _ __size: Int, _ __nitems: Int, _ __stream: UnsafeMutablePointer!) -> Int     public funcfwrite(_ __ptr: UnsafeRawPointer!, _ __size: Int, _ __nitems: Int, _ __stream: UnsafeMutablePointer!) -> Int     public funcfseek(_: UnsafeMutablePointer!, _: Int, _: Int32) -> Int32     public funcftell(_: UnsafeMutablePointer!) -> Int     public funcfeof(_: UnsafeMutablePointer!) -> Int32  </code></pre>    <p>由这些接口提供文件操作功能,在此基础上做 Swift 封装,以完成文件操作。</p>    <h2>API</h2>    <p>本库的接口分为了 标准API 以及 扩展API ,具体可以直接下载源码查看。</p>    <ul>     <li>标准API:基本文件操作接口,可以完成所有文件操作。</li>     <li>扩展API:由标准API扩展的接口。例如 readInt 、 readDouble 等的简单封装,以及以协议扩展的 readObject 和 writeObject 接口。</li>    </ul>    <h2>协议扩展</h2>    <p>框架包含了两个可扩展的协议来完成用户自定义对象对文件的读取和写入。</p>    <ul>     <li>EMFileStreamReadable</li>    </ul>    <pre>  <code class="language-swift">public protocol EMFileStreamReadable {      public static funcemObjectRead(withStreamstream: EMFileStream.EMFileStream) throws -> EMFileStreamReadable  }  </code></pre>    <p>实现此协议后由文件流构造一个自定义对象。</p>    <ul>     <li>EMFileStreamWriteable</li>    </ul>    <pre>  <code class="language-swift">public protocol EMFileStreamWriteable {      public funcemObjectWrite(withStreamstream: EMFileStream.EMFileStream) throws  }  </code></pre>    <p>实现此协议后将对象保存到文件流。</p>    <p>例:</p>    <p>Student 类实现了文件流读写协议:</p>    <pre>  <code class="language-swift">importFoundation  importEMFileStream     class Student: EMFileStreamReadable, EMFileStreamWriteable {         var name: String      var age: Int      var source: Float      var doubleSource: Double      var memo: String         init(name: String, age: Int, source: Float, doubleSource: Double, memo: String) {          self.name = name          self.age = age          self.source = source          self.doubleSource = doubleSource          self.memo = memo      }         funcemObjectWrite(withStreamstream: EMFileStream) throws {          try stream.write(string: name, writeSize: 20)          try stream.write(int: age)          try stream.write(float: source)          try stream.write(double: doubleSource)          try stream.write(string: memo, writeSize: 100)      }         static funcemObjectRead(withStreamstream: EMFileStream) throws -> EMFileStreamReadable {          letname = try stream.readString(withSize: 20)          letage = try stream.readInt()          letsource = try stream.readFloat()          letdoubleSource = try stream.readDouble()          letmemo = try stream.readString(withSize: 100)          return Student.init(name: name, age: age, source: source, doubleSource: doubleSource, memo: memo)      }  }  </code></pre>    <p>保存该对象到文件流:</p>    <pre>  <code class="language-swift">    letstudent = Student.init(name: "Sark", age: 20, source: 78.9, doubleSource: 90.12345, memo: "Memo..........")      do {          letfile = try EMFileStream.init(path: path, mode: .writeBin)          try file.write(object: student)      } catch {          print(error)      }  </code></pre>    <p>从文件流读取该对象:</p>    <pre>  <code class="language-swift">    do {          letfile = try EMFileStream.init(path: path, mode: .readBin)          letstudent = try file.readObject(cls: Student.self)          print(student)      } catch {          print(error)      }  </code></pre>    <p>测试结果:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/362ec80d29579ad7d5b7b93d6c4a7a03.png"></p>    <p> </p>    <h2>错误</h2>    <p>stdio 中发生的错误由私有方法 swiftwrap_errormsg() 截获,经过 封装 后抛出。所有抛出的错误类型都被封装成了 EMError 对象,其结构为:</p>    <pre>  <code class="language-swift">openclass EMError: Error, CustomStringConvertible {         openvar name: String      openvar detail: String         public var description: String {          return "name: \(name), detail: \(detail)"      }         public init(type: EMErrorType, detail: String) {          name = type.rawValue          self.detail = detail      }  }  </code></pre>    <p>错误中包含了错误名称以及错误描述,以便开发者调试。</p>    <h2>结语</h2>    <p>这是一个简单的工具库,可以很方便的操作文件流。在文件太大全部读进内存操作不现实的情况下可以带来不错的效果。同时 EMFileStreamReadable 和 EMFileStreamWriteable 协议可以带来 NSCoding 一部分功能,持久化归档对象到文件,又可以快速从文件恢复出对象。相对 NSCoding 来说,又有多平台通用性,同时也脱离了 NSCoding类绑定 的特性。希望能给 Swift 开发者们带来一丝便利。</p>    <p> </p>    <p> </p>