[Swift]coding的一些技巧(瞎编中...)

1,350 阅读4分钟

总结一下多年来所学习到的 Swift 编码技巧

1.从 Data 中获取 bytes

///OutStream 中有一个 
open func write(_ buffer: UnsafePointer<UInt8>, maxLength len: Int) -> Int 方法

如何将数据以流的形式存储呢

let data = Data
///事实上 data.bytes 其实就是 unsigned char *的数组 在 Swift 中可以用[UInt8]数组来表示
///或者使用(data as NSData).bytes 并不优雅  毕竟要尽量脱离OC的框架
///data是结构体  使用[UInt8]构造方法得到data的byte数组
let bytes = [UInt8](data)
///使用UnsafePointer<UInt8>构造方法生成指针
outputStream?.write(UnsafePointer<UInt8>(bytes), maxLength: bytes.count)

2.遍历元组

按道理元组是没必要遍历的,但是在 swift 中还是有一些应用场景的,比如获取设备型号

///在结构体utsname中 设备型号machine的定义如下(在OC中是一个char *指针)
 public var machine: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) /* [XSI] Hardware type */

256个元素的元组 用 0,1,2,3 这样取要到什么时候,最然可能10位以前就能碰到\0提前退出了 可以使用反射来得到一个元素的数组

var systemInfo = utsname()
uname(&systemInfo)
let mirror = Mirror(reflecting: systemInfo.machine)
let type = mirror.children.map {String(UnicodeScalar(UInt8( $0.value as! Int8)))}.joined()
print(type)/// iPhone8,1

3.Swift 中 KVO 的使用

SwiftKVO 简单介绍

let view = UIView()
var observation = view.observe(\UIView.frame, options: [.new]) { (_, change) in
    ///frame的类型时CGRect  不像原来从字典取值还需要转换
    let frame = change.newValue
}
///invalidate() will be called automatically when an NSKeyValueObservation is deinited
/// 监听对象会在销毁的时候自动调用invalidate()方法的  所以要提前强引用监听对象 然后跟随引用者一起销毁就好了
observation.invalidate()

4.闭包中使用不会引起循环引用的 self

事实上这是一个 bug,但是开发者很喜欢 不需要再定义 strongSelf 这样的起个新名字的变量了 当然 这样闭包中也不会在拿到真正的 self

 var enclosure = {[weak self] in
    guard let `self` = self else {return}
    self.xxx 
}

5.Swift 自定义 log

func ASLog(_ value: Any... , fileName : String = #file,function : String = #function, line : Int32 = #line ){
    #if DEBUG
        print("文件名称:\((fileName as NSString).lastPathComponent) Function: \(function)  Line:\(line)")
        print("调试信息:\(value)")
        print("============================================")
        
    #endif
}

ASLog("哈哈")

文件名称:coding.playground Function: __lldb_expr_1  Line:31
调试信息:["哈哈"]
============================================

6. 抛异常,消 error (眼不见心不烦)

有返回值得方法 暂时可能不会写逻辑 提前 return 一个值就好了 也可以直接 fatalError() 这样你要是忘写代码了 还能直接给你崩溃

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        fatalError()
        ///fatalError("implement later")
}

7.方法中泛型的使用

泛型的好处不多说了

方法中标记了某个类型,返回值也是跟这个类型相关

这个泛型的类型在合适的时候确定即可

下面的例子则是由等号左边的值得类型确定的

方法举例:

定义一个解析工具类,传入一个 json 返回一个模型的数组 (HandyJSON 本身就可以用[T].self.self.deserialize(from: json)解析)

protocol HandyJSON {}

struct Book: HandyJSON {
    var id = 0
    var name = ""
}
struct Student: HandyJSON {
    var id = 0
    var name = ""
}
class Parse {
    class func returnModels<T: HandyJSON>(_ json: String) -> [T] {
        return []
    }
}
var res1: [Book] = Parse.returnModels("")
var res2: [Student] = Parse.returnModels("")
var res3: [Book]
res3 = Parse.returnModels("")

像 Array 的 map 方法也是一个带泛型函数

 @inlinable public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]

泛型的类型可以通过结果值来确定,也可以在闭包中返回值确定

var names1: [String] = res1.map { $0.name }
var names2 = res2.map { (s) -> String in
    return s.name
}

一句话:只需要在 T 出现的地方确定类型就可以了

8.使用 NOT 代替 ! 做非的判断(然并卵)

有的语言是用if NOT condition : expression来做非的判断

当然我会觉得!更简单一些

那么如何在 Swift 中实现这种非的判断呢,请看下面的代码,骚操作,无卵用

let NOT = (!)

let condition = true

if NOT(condition) {
    expression
}

9.Swift 中的 sizeof()

Swift3.0以后 sizeof()函数就已经不能使用了,不过可以使用MemoryLayout < T > 来获取数据占据内存空间

public enum MemoryLayout<T> {
    public static var size: Int { get } //连续的内存占用量,以字节为单位
    public static var stride: Int { get }//存储在连续存储器或存储器中的一个实例的开始到下一个实例的开始的字节数
    public static var alignment: Int { get }//默认对齐方式,以字节位单位
    public static func size(ofValue value: T) -> Int
    public static func stride(ofValue value: T) -> Int
    public static func alignment(ofValue value: T) -> Int
    public static func offset(of key: PartialKeyPath<T>) -> Int?
}


struct Point {
    let x: Double
    let y: Double
    let isFilled: Bool
}
// MemoryLayout<Point>.size == 17
// MemoryLayout<Point>.stride == 24
// MemoryLayout<Point>.alignment == 8

10.指针操作

在 Swift 中
UnsafePointer 对应 C 中的const Type * UnsafeMutablePointer 对应Type * 通过 Unsafe 也可以看出来指针操作是不安全的 Swift 中不能直接通过 & 直接得到一个指针变量,但是依然可以用&获取变量地址

func incrementor(_ ptr: UnsafeMutablePointer<Int>) {
    ptr.pointee += 1
}
var a = 10
incrementor(&a)
a  // 11

var arr = [11,22]
incrementor(&arr)
arr// [12,22]

可以通过+ - 操作来实现指针的位移,如下:

func incrementor(_ ptr: UnsafeMutablePointer<Int>) {
    (ptr+1).pointee += 1
//    ptr.pointee += 1
}
var a = 10
var b = 11
incrementor(&a)
a  // 10
b  // 12

var arr = [11,22]
incrementor(&arr)
arr // [11,23]

11.Swift 中的 autoreleasepool

原理同 OC 中的 @autoreleasepool

public func autoreleasepool<Result>(invoking body: () throws -> Result) rethrows -> Result

func test() {
    for _ in 0..<999 {
        autoreleasepool {
            let a = loadBigData()
        }
    }
}

11.自定义输出实例信息

OC中 NSObject对象有 description 属性 可以在 NSLog 时自定义输出 在 swift 中需要遵守CustomStringConvertible这个协议

struct Person: CustomStringConvertible{
    var age: Int
    var name: String
    var description: String {
        "姓名: \(name) 年龄:\(age)"
    }
}

let p = Person(age: 18, name: "小明")
print(p) ///姓名: 小明 年龄:18
print("\(p)")///姓名: 小明 年龄:18