Swift CustomStringConvertible 協定的使用

2023-04-21 18:01:31

一、前言

先看一下Swift標準庫中對CustomStringConvertible協定的定義

public protocol CustomStringConvertible {
    /// A textual representation of this instance.
    ///
    /// Calling this property directly is discouraged. Instead, convert an
    /// instance of any type to a string by using the `String(describing:)`
    /// initializer. This initializer works with any type, and uses the custom
    /// `description` property for types that conform to
    /// `CustomStringConvertible`:
    ///
    ///    struct Point: CustomStringConvertible {
    ///        let x: Int, y: Int
    ///
    ///        var description: String {
    ///            return "(\(x), \(y))"
    ///        }
    ///    }
    ///
    ///    let p = Point(x: 21, y: 30)
    ///    let s = String(describing: p)
    ///    print(s)
    ///    // Prints "(21, 30)"
    ///
    /// The conversion of `p` to a string in the assignment to `s` uses the
    /// `Point` type's `description` property.
    var description: String { get }
}

從宣告中我們可以看到協定中只包含了一個 description的唯讀屬性 ,而且通過協定命名也可以窺探到它的作用 Custom+String+Convertible (所作用的型別去自定義String的轉換)

實現CustomStringConvertible協定類似於在Objective-C中重寫description方法, 可用於:

  • 自定義作用型別的print輸出
  • 作用的型別可自定義轉換成String

如標準庫中給的範例,拿出來分析一下:

struct Point: CustomStringConvertible {
    let x: Int, y: Int

    var description: String {
        return "(\(x), \(y))"
    }
}

let p = Point(x: 21, y: 30)
let s = String(describing: p)
print(s)


// Prints "(21, 30)"

上例中結構體Point 實現了CustomStringConvertible協定, 完成了description屬性的實現, 返回自定義字串 "((x), (y))"。 接著使用String型別的 String(describing: p ) 初始化方法完成了 Point結構體轉成指定String型別格式的轉換。

通過上面的介紹,我們基本上了解了CustomStringConvertible協定的用法, 接下來介紹幾種使用場景。

首先要知道的是 -- 在Swift中可以實現協定的型別有 結構體列舉。 也就是說只有結構體、 類、 列舉等型別都可以實現CustomStringConvertible協定

二、使用場景

1. 整型型別的列舉使用

enum AudioStatus: Int {
	case stopped = 0, playing, recording, interruptionPlaying, interruptionRecording
}

如果在使用列舉時,除了需要存取列舉的整型值外,還需要可以方便的輸出每個列舉對應的字串型別的狀態。 那麼在這種場景下,通過extension擴充套件列舉,並實現CustomStringConvertible協定將會很合適

extension AudioStatus : CustomStringConvertible {
    
    var description: String {
        switch self  {
        case .stopped:
            return "Audio: Stopped"
        case .playing:
            return "Audio: Playing"
        case .recording:
            return "Audio: Recording"
        case .interruptionPlaying:
            return "Audio: interruptionPlaying"
        case .interruptionRecording:
            return "Audio: interruptionRecording"
        }
    }
}

使用:

let status:AudioStatus = .stopped
let audioName = String(describing:status)  //取整型列舉對應的 字串值
print(「audioName:\(audioName)」)

2. Class型別的使用

定義一個類的話, 當我們使用print 時候並不會輸出類中的變數

class Wheel {
    var spokes: Int = 0
    var diameter: Double = 0.0
    
    init(spokes:Int = 32,diameter:Double = 26.0) {
        self.spokes = spokes
        self.diameter = diameter
    }
    
    func removeSpokes() {
        spokes = spokes > 0 ? spokes-- : spokes
    }
}

var wheel = Wheel(spokes: 36,diameter: 29)
print(wheel)
/**
 *  "Wheel\n"
 */

如果想要改變 print 的輸出結果,我們需要讓類遵守這個協定,最好用 extension擴充套件

extension Wheel: CustomStringConvertible {
    var description: String {
        return "wheel has \(spokes) spokes"
    }
}
var wheel = Wheel(spokes: 36,diameter: 29)
print(wheel)
/**
 *  "wheel has 36 spokes\n"
 */

如果想了解更多內容,可以參見專欄 《ios開發你需要知道的》