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

发布于:2016-12-25 14:56,阅读数:1866,点赞数:13


> 这是一款基于Swift3.0的文件流操作库
>
> 项目在[「GitHub」](https://github.com/trmbhs/EMFileStream)中开源,可在`iOS`、`macOS`和`Linux`中通用。

# 引言

由于项目原因,将一些用`C++`实现的库移植到了`iOS`中。移植过程必然造了不少轮子,本文将开源一个基于`stdio`的`Swift`文件流操作库。底层由`C语言`接口实现,可以简化`Swift`对文件流的操作过程。

这个库不仅适用于`Swift`开发的`iOS App`,也同样适用于`Swift`开发的`macOS`和`Linux`程序。

# 实现

由于底层功能由`C语言`实现,文件读写都依赖缓冲区实现,所以就会用到大量指针操作,这对`Swift`来说简直就是灾难。因此我造了一些轮子来避免指针操作以及使ARC也能管理这些内存。

## EMMemory

该工具类将接管所有游离的内存缓冲区,封装过后使缓冲区拥有一定的C指针功能,同时使ARC得以维护这段内存。

作为ARC的媒介封装,在构造函数中分配内存,在析构函数中释放内存,使得C分配的缓冲区内存得以管理:

```swift
open class EMMemory {

open var mptr: UnsafeMutablePointer<UInt8>

open var size: Int = 0

public init(size: Int) {
self.mptr = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
self.size = size
}

deinit {
mptr.deallocate(capacity: size)
}

}
```

  添加下标使其获得C语言指针的能力:

```swift
open subscript(index: Int) -> UnsafeMutablePointer<UInt8> {
return mptr.advanced(by: index)
}
```

  最后完善Swift不可变指针属性和数据数组属性:

```swift
open var iptr: UnsafePointer<UInt8> {
get {
return UnsafePointer<UInt8>.init(mptr)
}
}

open var data: Array<UInt8> {
get {
var data = Array<UInt8>.init(repeating: 0, count: size)
for i in 0..<size {
data[i] = mptr.advanced(by: i).pointee
}
return data
}
}
```

有了这个类以后,我们不再使用`allocate`分配内存区块,由创建该对象接管。

## stdio文件操作

`stdio`中对于文件的操作函数有很多,本框架主要用到如下几个函数:

```swift
public func fopen(_ __filename: UnsafePointer<Int8>!, _ __mode: UnsafePointer<Int8>!) -> UnsafeMutablePointer<FILE>!

public func fclose(_: UnsafeMutablePointer<FILE>!) -> Int32

public func fread(_ __ptr: UnsafeMutableRawPointer!, _ __size: Int, _ __nitems: Int, _ __stream: UnsafeMutablePointer<FILE>!) -> Int

public func fwrite(_ __ptr: UnsafeRawPointer!, _ __size: Int, _ __nitems: Int, _ __stream: UnsafeMutablePointer<FILE>!) -> Int

public func fseek(_: UnsafeMutablePointer<FILE>!, _: Int, _: Int32) -> Int32

public func ftell(_: UnsafeMutablePointer<FILE>!) -> Int

public func feof(_: UnsafeMutablePointer<FILE>!) -> Int32
```

由这些接口提供文件操作功能,在此基础上做`Swift`封装,以完成文件操作。

## API

本库的接口分为了`标准API`以及`扩展API`,具体可以直接下载源码查看。

- 标准API:基本文件操作接口,可以完成所有文件操作。
- 扩展API:由标准API扩展的接口。例如`readInt`、`readDouble`等的简单封装,以及以协议扩展的`readObject`和`writeObject`接口。

## 协议扩展

框架包含了两个可扩展的协议来完成用户自定义对象对文件的读取和写入。

- `EMFileStreamReadable`

```swift
public protocol EMFileStreamReadable {
init(stream: EMFileStream) throws
}
```

实现此协议后由文件流构造一个自定义对象。

- `EMFileStreamWriteable`

```swift
public protocol EMFileStreamWriteable {
public func emObjectWrite(withStream stream: EMFileStream) throws
}
```

实现此协议后将对象保存到文件流。

例:

`Student`类实现了文件流读写协议:

```swift
import Foundation
import EMFileStream

class Student: CustomStringConvertible, EMFileStreamReadable, EMFileStreamWriteable {

var name: String
var age: Int
var source: Float
var memo: String

var description: String {
return "name: \(name), age: \(age), source: \(source) memo: \(memo)"
}

init(name: String, age: Int, source: Float, memo: String) {
self.name = name
self.age = age
self.source = source
self.memo = memo
}

required init(stream: EMFileStream) throws {
self.name = try stream.readString(withSize: 20)
self.age = try stream.readInt()
self.source = try stream.readFloat()
self.memo = try stream.readString(withSize: 100)
}

func emObjectWrite(withStream stream: EMFileStream) throws {
try stream.write(string: name, writeSize: 20)
try stream.write(int: age)
try stream.write(float: source)
try stream.write(string: memo, writeSize: 100)
}
}
```

保存该对象到文件流:

```swift
let student = Student.init(name: "Sark", age: 20, source: 78.9, doubleSource: 90.12345, memo: "Memo..........")
do {
let file = try EMFileStream.init(path: path, mode: .writeBin)
try file.write(object: student)
} catch {
print(error)
}
```

从文件流读取该对象:

```swift
do {
let file = try EMFileStream.init(path: path, mode: .readBin)
let student: Student = try file.readObject()
print(student)
} catch {
print(error)
}
```

测试结果: ![16011_1](//cdn.yuusann.com/img/posts/16011_1.png)

## 错误

`stdio`中发生的错误由私有方法`swiftwrap_errormsg()`截获,经过`封装`后抛出。所有抛出的错误类型都被封装成了`EMError`对象,其结构为:

```swift
open class EMError: Error, CustomStringConvertible {

open var name: String
open var detail: String

public var description: String {
return "name: \(name), detail: \(detail)"
}

public init(type: EMErrorType, detail: String) {
name = type.rawValue
self.detail = detail
}
}
```

错误中包含了错误名称以及错误描述,以便开发者调试。

# 结语

这是一个简单的工具库,可以很方便的操作文件流。在文件太大全部读进内存操作不现实的情况下可以带来不错的效果。同时`EMFileStreamReadable`和`EMFileStreamWriteable`协议可以带来`NSCoding`一部分功能,持久化归档对象到文件,又可以快速从文件恢复出对象。相对`NSCoding`来说,又有多平台通用性,同时也脱离了`NSCoding类绑定`的特性。希望能给`Swift`开发者们带来一丝便利。


评论:0条


返回列表

返回归档

返回主页