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

bmdb5781 7年前發布 | 6K 次閱讀 Swift Apple Swift開發

這是一款基于Swift3.0的文件流操作庫

引言

由于項目原因,將一些用 C++ 實現的庫移植到了 iOS 中。移植過程必然造了不少輪子,本文將開源一個基于 stdio 的 Swift 文件流操作庫。底層由 C語言 接口實現,可以簡化 Swift 對文件流的操作過程。

這個庫不僅適用于 Swift 開發的 iOS App ,也同樣適用于 Swift 開發的 macOS 和 Linux 程序。

實現

由于底層功能由 C語言 實現,文件讀寫都依賴緩沖區實現,所以就會用到大量指針操作,這對 Swift 來說簡直就是災難。因此我造了一些輪子來避免指針操作以及使ARC也能管理這些內存。

EMMemory

該工具類將接管所有游離的內存緩沖區,封裝過后使緩沖區擁有一定的C指針功能,同時使ARC得以維護這段內存。

作為ARC的媒介封裝,在構造函數中分配內存,在析構函數中釋放內存,使得C分配的緩沖區內存得以管理:

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

添加下標使其獲得C語言指針的能力:

    opensubscript(index: Int) -> UnsafeMutablePointer {
        return mptr.advanced(by: index)
    }

最后完善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..

有了這個類以后,我們不再使用 allocate 分配內存區塊,由創建該對象接管。

stdio文件操作

stdio 中對于文件的操作函數有很多,本框架主要用到如下幾個函數:

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

由這些接口提供文件操作功能,在此基礎上做 Swift 封裝,以完成文件操作。

API

本庫的接口分為了 標準API 以及 擴展API ,具體可以直接下載源碼查看。

  • 標準API:基本文件操作接口,可以完成所有文件操作。
  • 擴展API:由標準API擴展的接口。例如 readInt 、 readDouble 等的簡單封裝,以及以協議擴展的 readObject 和 writeObject 接口。

協議擴展

框架包含了兩個可擴展的協議來完成用戶自定義對象對文件的讀取和寫入。

  • EMFileStreamReadable
public protocol EMFileStreamReadable {
    public static funcemObjectRead(withStreamstream: EMFileStream.EMFileStream) throws -> EMFileStreamReadable
}

實現此協議后由文件流構造一個自定義對象。

  • EMFileStreamWriteable
public protocol EMFileStreamWriteable {
    public funcemObjectWrite(withStreamstream: EMFileStream.EMFileStream) throws
}

實現此協議后將對象保存到文件流。

例:

Student 類實現了文件流讀寫協議:

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

保存該對象到文件流:

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

從文件流讀取該對象:

    do {
        letfile = try EMFileStream.init(path: path, mode: .readBin)
        letstudent = try file.readObject(cls: Student.self)
        print(student)
    } catch {
        print(error)
    }

測試結果:

 

錯誤

stdio 中發生的錯誤由私有方法 swiftwrap_errormsg() 截獲,經過 封裝 后拋出。所有拋出的錯誤類型都被封裝成了 EMError 對象,其結構為:

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
    }
}

錯誤中包含了錯誤名稱以及錯誤描述,以便開發者調試。

結語

這是一個簡單的工具庫,可以很方便的操作文件流。在文件太大全部讀進內存操作不現實的情況下可以帶來不錯的效果。同時 EMFileStreamReadable 和 EMFileStreamWriteable 協議可以帶來 NSCoding 一部分功能,持久化歸檔對象到文件,又可以快速從文件恢復出對象。相對 NSCoding 來說,又有多平臺通用性,同時也脫離了 NSCoding類綁定 的特性。希望能給 Swift 開發者們帶來一絲便利。

 

 

 本文由用戶 bmdb5781 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!