條件編譯
這是 Swift 的條件編譯(Conditional Compilation)的一個示例,它允許你根據特定的條件選擇性地編譯代碼。在這段代碼中,它確定代碼是在 iOS 模擬器中運行還是在實際的 iOS 裝置上運行。
#if targetEnvironment(simulator)
self.videoCapturer = RTCFileVideoCapturer(delegate: videoSource)
#else
self.videoCapturer = RTCCameraVideoCapturer(delegate: videoSource)
#endif
判斷變數是否存在
Swift的空值會是nil
什麼 是 Protocol 與 Delegate
1. Protocol
在 Swift 中,協議(Protocol
)定義了一套規範或合約,但不提供具體的實現。任何型別(如 class
、struct
或 enum
)都可以遵循(implement)這些協議,並為協議中的要求提供具體的實現。
例如,我們可以定義一個表示可序列化對象的 Serializable
協議:
protocol Serializable {
func serialize() -> String
}
然後,我們可以讓某個 struct
或 class
遵循這個協議:
struct Person: Serializable {
var name: String
var age: Int
func serialize() -> String {
return "\(name),\(age)"
}
}
2. Delegate
Delegate
是一種設計模式,允許一個物件將某些決策或功能外包給另一個物件。在 Swift 中,Delegate
通常是通過協議來實現的。這意味著當一個類想要成為另一個類的代理時,它必須遵循一個特定的協議。
例如,假設我們有一個 Downloader
類,當下載完成時,我們希望通知另一個物件。我們可以定義一個 DownloaderDelegate
協議:
protocol DownloaderDelegate: AnyObject {
func didFinishDownloading(data: Data)
}
然後,Downloader
類可以有一個 delegate
屬性:
class Downloader {
weak var delegate: DownloaderDelegate?
func download() {
// 下載過程...
// 完成後通知代理:
delegate?.didFinishDownloading(data: someData)
}
}
其他類(例如 ViewController)可以遵循 DownloaderDelegate
協議並成為 Downloader
的代理,以響應下載完成事件。
這兩個概念在 iOS 和 Swift 開發中都是核心的,用於很多不同的場景和功能。
強大的extension
在 Swift 中,類別 (class)、結構體 (struct)、列舉 (enum)、或者協定 (protocol) 都可以使用 extension
來增加功能。這是 Swift 中一個非常強大的功能,使得你可以將類別或結構體的定義分散到多個區塊,使其更具組織性和可讀性。
以下是使用 extension
的一些常見原因:
- 組織性: 你可以使用
extension
來組織你的程式碼,將相似的功能或者與某個特定主題相關的方法放在一起。 - 遵循協定: 如果你的類別需要遵循某個協定 (protocol),你可以使用
extension
來隔離所有與該協定相關的實現。 - 可讀性: 將類別或結構體分成小區塊可以提高程式碼的可讀性。
- 重用和模組化: 你可以定義多個
extension
,並將它們放在不同的檔案中,以便於重用或者模組化。
在你給出的例子中,原始的 WebRTCClient
類別可能已經有了一些基本功能。然後,通過使用 extension
,增加了關於視頻控制的功能。這種方式可以讓每個 extension
專注於一個特定的功能或者主題,從而使得整體的程式碼更加清晰和有組織性。
總的來說,Swift 中的 extension
功能允許我們對既有的類別或其他資料結構進行進一步的擴展,這樣可以使得程式碼更具組織性、可讀性,並且更容易維護。
分開寫 extension
和將其寫在一起,從功能的角度來看,並沒有區別。不過,程式碼的組織方式往往是為了達到更好的可讀性和易於維護。將不同的功能或與特定主題相關的方法分開在不同的 extension
中,可以幫助讀者和開發者更快地了解該區塊的主要功能,也更容易定位和修改相關的代碼。
使用guard控制流結構
guard
是 Swift 語言中的一個控制流結構,用於提前退出(early exit)方法或函數的執行,當某些條件不成立時。它是為了減少嵌套(nested)的深度和提高程式碼的可讀性。
以下是 guard
的一些特點:
guard
之後必須跟一個條件,如果這個條件為false
,則執行else
裡面的代碼。- 在
else
裡面必須有一個轉移控制語句,例如return
、break
、continue
或throw
,以確保退出當前的執行流程。 - 如果
guard
的條件為true
,則會繼續執行guard
之後的代碼。
例如:
guard let dataToSend = alert.textFields?.first?.text?.data(using: .utf8) else {
return
}
這段代碼試圖從 alert
的第一個文本欄位中獲取文本,然後將其轉換為 UTF-8
編碼的 Data
對象。如果其中任何一步失敗(例如,文本欄位為 nil
、文本為空或轉換失敗),則整個條件為 false
,並執行 else
裡面的 return
語句,從當前函數或方法中提前退出。
使用 guard
而不是嵌套的 if-let
可以使程式碼更加整潔、易讀,特別是當有多個條件需要檢查時。
在Swift中使用閉包
在Swift中,$0, $1, $2, … 是在閉包中使用的隱式名稱,代表閉包的第一個、第二個、第三個…參數。
peerConnection.transceivers
.compactMap { return $0.sender.track as? T }
.forEach { $0.isEnabled = isEnabled }
這裡有兩個閉包:一個是用於 compactMap 的閉包,另一個是用於 forEach 的閉包。
- 對於 compactMap 的閉包:$0 代表 peerConnection.transceivers 集合中的每一個元素,即每一個 RTCRtpTransceiver 對象。在這個閉包中,它試圖取出每個 transceiver 的 sender 的 track 並嘗試將其轉型為指定的 T 類型。
- 對於 forEach 的閉包:在 compactMap 運行之後,我們獲得一個包含符合指定類型 T 的軌道的集合。在 forEach 的閉包中,$0 代表這些軌道。閉包的作用是設置這些軌道的 isEnabled 屬性。