Swift 开发 Linux 和 macOS 应用的利器 —— PerfectLib 食用全指南

发布于: 2017-02-11 22:48
阅读: 3119
评论: 0
喜欢: 14

「Perfect」是一款 Swift 服务端框架,而「PerfectLib」是其重要组件之一

引言

PerfectLibPerfect的重要组件。一个服务端软件离不开和文件、系统打交道,正是PerfectLib在背后默默支撑着。而这个组件是一个独立的 Module,是可以单独使用的。这就意味着即使开发本地软件也是可以拿来用的。

那么今天就不讲服务端,只讲这个组件。

功能

「官方文档基本工具页」中,一部分内容是留给PerfectLib的,中文文档这样描述它:

一系列用于搭建服务器和客户机应用程序所需要的一系列工具类基本函数库。

因此在开发客户机程序时候也可以使用这些工具,能大大提高开发效率。最重要的是,在 macOS 下通用。由于大多数基于C语言接口开发,在目前情况看,兼容性比 Swift 标准库好。

组分

截止笔者写文时,这一栏包含:

下面就来分别看看这些工具类是如何使用的。

Bytes —— 字节流

其结构包含一个UInt8数组用于存放数据和一个position属性。

/// The position from which new export operations begin.
public var position: Int
/// The underlying UInt8 array
public var data: [UInt8]

其包含一组import和一组export方法来处理字节流。

public func import8Bits(from frm: UInt8) -> PerfectLib.Bytes
public func import16Bits(from frm: UInt16) -> PerfectLib.Bytes
public func import32Bits(from frm: UInt32) -> PerfectLib.Bytes
public func import64Bits(from frm: UInt64) -> PerfectLib.Bytes
public func importBytes(from frm: [UInt8]) -> PerfectLib.Bytes .Bytes
......
    
public func export8Bits() -> UInt8
public func export16Bits() -> UInt16
public func export32Bits() -> UInt32
public func export64Bits() -> UInt64
public func exportBytes(count cnt: Int) -> [UInt8]
......

它是以字节队列形式处理。当import字节时,加入的数据会被appenddata结尾,而当输出时会从数组开头进行输出并且移动position位置。输出的数据并不会被删除,仅仅是移动了position使下次输出时能从上次的位置继续输出。因此此工具不适合处理大量数据以及拥有过长的生命周期,仅限于临时使用。

基于它直接操作字节的特性,可以用它来拼接整形:

let bytes = Bytes.init()
bytes.importBytes(from: [0x01, 0xed])
print(bytes.export16Bits())
//输出:60673

需要注意的是数字大小端的问题,可以从这个例子看出大小端:

bytes.importBytes(from: [0x01, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
print(bytes.export64Bits())
//输出:60673
let bytes = Bytes.init()
bytes.importBytes(from: [0x01, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])
print(bytes.export64Bits())
//输出:72057594037988609

上面例子中仅有最后一位数据不同,很容易就能看出大小端了。

Dir —— 目录与路径

利用Dir可以对目录进行基本的操作。但事实上Dir并不是一个目录的抽象,而是一个路径的抽象。构造一个Dir对象并不意味着一定要有这个目录,相反的这个目录可以是不存在的。因此用路径来称呼更加合适一点。

下面笔者将分组进行分析帮助读者理解:

基本

这组属性包含了路径常规的属性:

public var exists: Bool { get }
public var name: String { get }
public var path: String { get }
public var parentDir: PerfectLib.Dir? { get }

权限

权限类型被定义为了PermissionMode,实际为C语言桥接的mode_t类型。而这个类型在 masOS 叫__darwin_mode_t,实际数据为一个16位无符号整形。

perms属性记录了目录的权限:

public typealias PermissionMode = PerfectLib.File.PermissionMode
public var perms: PerfectLib.Dir.PermissionMode

操作和遍历

创建、删除以及遍历方法。这三个方法都是基于C语言接口的,macOS 中接口存在于Darwin中,如果失败都会抛出异常。当C语言接口返回异常值时框架会调用strerror方法获取错误信息并封装成PerfectError.fileError异常抛出。

public func create(perms: PerfectLib.Dir.PermissionMode = default) throws
public func delete() throws
public func forEachEntry(closure: (String) throws -> ()) throws

辅助

一个用来存储工作目录的静态属性,方便开发者使用。

public static var workingDir: PerfectLib.Dir { get }
public func setAsWorkingDir() throws

File —— 文件操作

这是一个很全的文件操作库。不仅包含了针对文件的操作,还包含了对文件流的操作。它可以以数据流的形式对文件进行改写、追加等操作,这跟笔者的一个工具库功能十分相似:EMFileStream —— 基于 stdio 的 Swift 文件流操作库

下面读者也将对属性和方法进行分类分析:

基本

一组从属性名就能看出是什么的基本属性。比较眼生的大概也就只有fd,文件描述,笔者也喜欢称它为句柄,打开一个文件时的一个token

public var fd: Int
public var exists: Bool { get }
public var isDir: Bool { get }
public var path: String { get }
public var modificationTime: Int { get }
public var size: Int { get }
public var perms: PerfectLib.File.PermissionMode

符号链接属性

如果该文件是个符号链接symbolic link,那么这两个属性能帮助开发者找到它指向的目标。

public var isLink: Bool { get }
public var realPath: String { get }

文件操作

同样是一组从名字就能看出的功能,眼生的大概有:

  • marker: 代表目前文件流读取位置距离文件头位置的偏移量,底层实现使用了一个特别特别的方式。对于C语言文件操作比较熟悉的读者应该知道获取偏移量通常会使用ftell这个函数,而这里的实现使用了lseek函数。lseek是一个移动动作,通过参数选项能使文件指针从文件头、尾、当前位置其中之一为基准进行偏移,同时返回离文件头的距离。框架作者使用lseek从当前位置移动了0个字节来获取偏移量,这个做法有点诡异,大概是出于I/O性能考虑?

  • abandon: 重置文件状态,如果文件是打开状态就关闭它。

public var marker: Int
public var isOpen: Bool { get }
public func open(_ mode: PerfectLib.File.OpenMode = default, permissions: PerfectLib.File.PermissionMode = default) throws
public func close()
public func delete()
public func abandon()
public func moveTo(path: String, overWrite: Bool = default) throws -> PerfectLib.File
public func copyTo(path pth: String, overWrite: Bool = default) throws -> PerfectLib.File

流读写

这几个方法能以流的形式从当前位置进行文件读写。但笔者使用的版本中并没有看到seek相关的方法,实现是非常容易的但是如果没有seek就失去了随机读写的功能。

public func readSomeBytes(count: Int) throws -> [UInt8]
public func readString() throws -> String
public func write(string: String) throws -> Int
public func write(bytes: [UInt8], dataPosition: Int = default, length: Int = default) throws -> Int

文件锁定

使用C语言方法lockf锁定文件。需要注意的是这个锁只会影响当前进程,对其他进程是无效的。跟NSLock类似的,lock方法也是阻塞式的。使用中的文件在解锁之前,新的锁定方法会被阻塞,当文件关闭时锁定会被自动解除。

public func lock(byteCount: Int) throws
public func unlock(byteCount: Int) throws
public func tryLock(byteCount: Int) throws
public func testLock(byteCount: Int) throws -> Bool

SysProcess —— 系统进程

这是一个非常Powerful的工具,它可以调用其他程序,甚至运行脚本。在服务端开发中这可以说是必不可少的一个工具。

它纯粹基于C语言实现,而在这之前,需要实现这个功能需要使用以下这个拥有三个名字的类实现,并通过管道获得输入输出。以下这三个类都是同一个东西,接口都是一样的,只是适用场合不同:

  • NSTask: 原 Objective-C 类
  • Process: 在 macOS 中的 Swift 类
  • Task: 在 Linux 中的 Swift 类

以下笔者也将分组进行分析。

基本

以命令、参数和环境参数的形式构造进程。进程一旦被构造就立即开始运行了,此时 Swift 程序是不会被阻塞的,程序还将继续运行。pid属性包含了新进程的pid

public var pid: pid_t
public init(_ cmd: String, args: [String]?, env: [(String, String)]?) throws
public func isOpen() -> Bool

标准输入、输出和错误

标准输入、输出和错误以文件的形式被定义,利用上面提到的文件流操作在这里可以进行交互。需要注意的是当没有输出时文件读取方法会被阻塞,而当进程结束时读取方法会抛出错误跳出。但当前版本在 Linux 下存在问题,在进程结束时阻塞的文件读取方法并没有按预期抛出异常,导致程序永久阻塞。

public var stdin: PerfectLib.File?
public var stdout: PerfectLib.File?
public var stderr: PerfectLib.File?

进程操作

当进程被构造时会立即开始运行,如需要等待进程返回需要使用wait方法等待进程返回。而killclose方法会杀死进程。detach方法负责进程脱离。

public func wait(hang: Bool = default) throws -> Int32
public func kill(signal: Int32 = default) throws -> Int32
public func close()
public func detach()

日志 —— Log

框架所带的日志包含两个内容:

  • ConsoleLogger: 控制台输出
  • SysLogger: 调用C接口vsyslog写入日志

这两部分遵守了Logger协议,包含:

func debug(message: String)
func info(message: String)
func warning(message: String)
func error(message: String)
func critical(message: String)
func terminal(message: String)

以不同的TAG输出不同的内容

其他

除此之外,PerfectLib还包含了以下组件:

  • JSONConvertible: 不怎么好用的 JSON 解析工具,不推荐。
  • Utilities: 一些系统类的扩展,如想知道细节请直接读源码。

结语

其实Perfect虽然是一款服务端框架,但用于支撑服务端的组件并不是都跟服务端开发有关。这些基本的工具也是能在日常客户端开发中使用的。另外笔者也开过脑洞,尝试过在iOS上使用。但SysProcess所依赖的接口在iOS下出了点问题,也没有深入研究去解决。

在 Linux 上开发 Swift 应用也是个比较新鲜的话题。有越来越多的优秀开源库出现,相信情况只会越来越好。拭目以待吧。


Thanks for reading.

All the best wishes for you! 💕