全文共8828字,預計學習時長16分鐘
本文介紹的這些Swift實用性技巧的操作說明可以用於創建iOS應用程式,希望你可從中有所收穫。
1. 可選項解包
var optional1: Int?
var optional2: Int?
// 1 - Ugly Code
func unwrap() {
if let o1 = optional1 {
if let o2 = optional2 {
// reached
}
}
}
// 2 - Clean Code
func unwrap() {
guard let o1 = optional1, let o2 = optional2 else { return }
// reached
}
解包可選變量,在一行中使用選項2處理故障。
如果兩個可選值都不是nil值,則選項2會被注釋。如果其中一個變量為nil值,則「返回」並退出此函數的範圍。
2. 三元運算符
let myBool = true
let testBool = myBool ? false : true
let testInt = myBool ? 16 : 8
//testBool equals false, testInt equals 16 because the input to the conditional is true, therefore
//the first choice is picked for each operator
//ternary
let ternary = conditional ? true : false
//vs. conditional
var ternary = false
if conditional {
ternary = true
}
通過這些有用的運算符,將條件邏輯壓縮到一行。根據狀態設置變量值。
是不是更方便?如果讀起來有點難,那麼可將這些運算符嵌套為緊湊的代碼。
//double stack
let ternaryDouble = conditional ? true : (conditional2 ? true : false)
//triple stack
let ternaryTriple = conditional ? true : (conditional2 ? true : (conditional3 ? true : false))
3. 泛型
// 1 - Ugly Code
var strings = ["Hello", "This", "Is", "A", "Test"]
var integers = [1, 2, 3, 4, 5, 6, 7]
func printStrings(_ array: [String]) {
for s in array {
print(s)
}
}
func printIntegers(_ array: [Int]) {
for i in array {
print(i)
}
}
// 1 - In Action
printStrings(strings)
printIntegers(integers)
// 2 - Clean Code
func printArray<T>(_ array: [T]) {
for item in array {
print(element)
}
}
// 2 - In Action
printArray(strings)
printArray(integers)
根據谷歌定義,泛型編程是一種編寫函數和數據類型的方法,同時對所使用的數據類型做出最小化假設。
考慮到抽象化,使用泛型可生成更為清晰的代碼,減少錯誤。
如上文所示,通過選擇選項2,可編寫一個函數(相對於多個),處理幾種不同類型的輸入。
4. 通過十六進位代碼生成UIColor
創建一個名為 UIColor+Extensions.swift的文件,包含以下代碼:
import UIKit
extension UIColor {
convenience init(hex:Int, alpha: CGFloat = 1.0) {
self.init(
red: CGFloat((hex & 0xFF0000) >> 16) / 255.0,
green: CGFloat((hex & 0x00FF00) >> 8) / 255.0,
blue: CGFloat((hex & 0x0000FF) >> 0) / 255.0,
alpha: alpha
)
}
}
恭喜,現在你可通過十六進位代碼生成不同顏色,如下所示:
let green = UIColor(hex: 0x1faf46)
let red = UIColor(hex: 0xfe5960)
let blue = UIColor(hex: 0x0079d5)
5. 使用擴展程序
import UIKit
extension UIButton {
func press(completion: @escaping() -> ()) {
UIView.animate(withDuration: 0.125, delay: 0, options: [.curveEaseIn], animations: {
self.transform = CGAffineTransform(scaleX: 0.94, y: 0.94)
}, completion: { _ in
UIView.animate(withDuration: 0.125, delay: 0, options: [.curveEaseIn], animations: {
self.transform = .identity
}, completion: { _ in
completion()
})
})
}
}
創建一個可擴展文件,包含經常重複使用的類。
在這種情況下,筆者選擇了UIButton演示添加自定義印刷功能。
現在,可在添加UIButton的地方,調用press函數模擬動畫進行放大和縮小,如下所示:
let myButton = UIButton()
myButton.press() {
//handle completion
}
6. 通過創建一個類匯集多種後端/函數調用
想像一下,你需要在應用程式內部調用一個函數來更新本地數據,如下所示:
FAB.updateData()
在該例中,FAB代表Google Firebase。現在,設想要清除Firebase,用另一個後端進行替換,這一技巧將使這種情況變得快速又簡單。
編寫代碼時,如果發現自己在應用程式中多次調用相同函數,那麼請創建一個類,將所調用函數「匯集」到一個函數中,然後調用你的網絡代碼。
例如:
// 1 - Bad Code
class MyClass1 {
init() {
FAB.updateData()
}
}
class MyClass2 {
init() {
FAB.updateData()
}
}
class MyClass3 {
init() {
FAB.updateData()
}
}
// 2 - Good Code
class Network {
func updateData() {
FAB.updateData()
}
}
class MyClass1 {
init() {
Network.updateData()
}
}
class MyClass2 {
init() {
Network.updateData()
}
}
class MyClass3 {
init() {
Network.updateData()
}
}
在選項1中,如果想要替換Firebase,需要切換出三個函數進行調用。在選項2中,只需要更新自己的網絡類。
7. guard let
除了解包選項,使用保護語句在其他方面也頗具實用性。可利用這些語句進行簡單的條件驗證檢查,以便在某些條件下將程序控制轉移到範圍之外。
例如:
// example 1 - nil checking
func checkTheText() {
guard let text = textField.text else {
return
}
//we made it this far... now we can use text for something!
updateLabel(text)
}
// example 2 - conditional checking
func conditionalCheck() {
let c1 = true
let c2 = false
let c3 = true
let c4 = true
guard c1, c2, c3, c4 else {
return
}
}
// example 3 - multiple validation checks
func validatePassword(_ password: String) -> Bool {
guard password.count >= 8 else { return false }
guard password.count <= 15 else { return false }
guard checkPasswordCharacters(password) else { return false } //must contain capital letter, special character, etc...
//password is valid
return true
}
8. 循環
// while loop
var i = 0
while 5 > i {
print(i) //output: 0 1 2 3 4
i += 1
}
// repeat
var a = 0
repeat {
print(a) //output: 0 1 2 3 4
a += 1
} while a < 5
// for loop
for c in 0...5 {
print(c) //output: 0 1 2 3 4 5
}
// for loop (no variable)
for _ in 0...3 {
print("count up") //output: count up, count up, count up, count up
}
// for loop (less than equal than)
for d in 0..<5 {
print(d) //output: 0 1 2 3 4
}
// for loop (reversed)
for z in (1..<10).reversed() {
print(z) //output: 9 8 7 6 5 4 3 2 1
}
// for loop (stride)
for g in stride(from: 1, to: 10, by: 3) {
print(g) //output: 1 4 7
}
// for loop (stride, reversed)
for k in stride(from: 3, to: 0, by: -1) {
print(k) //output: 3 2 1
}
明白易懂。多種用於創建循環的句法,並在旁邊列出相關輸出。
9. 使用枚舉確保切換語句/不同類別的項是類型安全的
// 1 - Ugly code
var marketShare: Int!
let operatingSystem = "iOS"
switch operatingSystem {
case "iOS": marketShare = 30
case "android": marketShare = 45
case "windows": marketShare = 15
case "sailfish": marketShare = 8
case "ubuntu": marketShare = 2
default: marketShare = 0
}
// 2 - Clean code
enum OS { case iOS, android, windows, sailfish, ubuntu }
var marketShare_: Int!
let operatingSystem_ = OS.iOS
switch operatingSystem_ {
case .iOS: marketShare_ = 30
case .android: marketShare_ = 45
case .windows: marketShare_ = 15
case .sailfish: marketShare_ = 8
case .ubuntu: marketShare_ = 2
}
在選項1中,可能會在switch語句中輸入一個不合適的字符串,會導致市場份額的設置值不合理。
在選項2中,我們強制此switch語句是類型安全的,因此無法輸入錯誤值並進行代碼編譯。
10. 使用回調發送完成處理程序
// 1 - Completion handlers
func myFunction(completion: @escaping() -> ()) {
UIView.animate(withDuration: 2, animations: {
//run animation
}, completion: { _ in
completion()
})
}
// 2 - Sending data through a callback: update UI upon network call
class Modal {
func getData(completion: ((_ data: String) -> Void)) {
let data = "Network data!"
completion(data)
}
}
class ViewController: UIViewController {
let model = Model()
override func viewDidLoad() {
super.viewDidLoad()
model.getData { [weak self] (data: String) in
self?.updateView(data)
}
}
private func updateView(_ data: String) {
print(data)
}
}
從選項1和選項2中可發現,可以在完成動作(動畫、網絡調用等)後發送警報,或者發送包含數據的警報。
11. 提供默認值
// "Hello World!" represents the default text we should use if the user's textInput is nil
// 1 - Ugly Code
var textInput: String?
var text = ""
if let t = textInput { text = t } else {
text = "Hello World!"
}
// 2 - Clean code
let text_ = textInput ?? "Hello World!"
在該例中,可發現兩個用於設置變量值的選項,具體取決於用戶輸入是否為nil。
12. 為便於訪問請將通用常量存儲在一個文件中
為便於使用,筆者喜歡將靜態常量存儲在一個文件中,如下所示:
import Foundation
struct Constants {
struct Colors {
static let blue = UIColor(hex: 0x111111)
static let green = UIColor(hex: 0x222222)
static let red = UIColor(hex: 0x333333)
}
struct Names {
static let myName = "Gavin"
static let myMomsName = "Marie"
static let myDadsName = "Jake"
static let mySistersName = "Jennifer"
}
}
例如,訪問UIColor:
let myColorPick = Constants.Colors.green
let sistersName = Constants.Names.mySistersName
13. 自動參考計數
強烈建議閱讀有關ARC(自動參考計數)的官方Swift文檔。
Swift使用ARC跟蹤和管理內存。這一點在使用應用程式中的幾種情況下需要牢記。
簡要介紹ARC在對象去初始化方面的影響:
試想,我們有Person這個類,Person:
class Person {
init() { print("initialized!") }
deinit { print("deinitialized!") }
}
接下來,創建三個變量。由於這三個變量是可選項,因此初始值為nil:
var ref1: Person? // nil
var ref2: Person? // nil
var ref3: Person? // nil
接下來,創建一個新的Person實例並將其分配至ref1。
ref1 = Person() // console output: "initialized!"
然後,指定ref2和ref3作為同一Person對象的參考:
ref2 = ref1 // Person
ref3 = ref1 // Person
既然所有三個參考都指向同一個Person對象,那麼我們可以將前兩個參考設置為nil,同時仍將Person對象保留在內存中,如下所示:
ref1 = nil
ref2 = nil
最後,要對Person對象去初始化,請將第三個和最後一個參考設置為nil:
ref3 = nil // console output: "deinitialized!"
14. 為函數參數提供默認參數
func printToConsole(_ messageLine1: String, _ messageLine2: String = "This is line 2!") {
print("\(messageLine1) | \(messageLine2)")
}
printToConsole("This is line 1!") // This is line 1! | This is line 2!
printToConsole("This is line one.", "This is line two.") //This is line one. | This is line two.
由上可見,為輸入參數提供默認值非常簡單。
15. 通過UserDefaults15.49/5000從內存中編碼/解碼結構
import Foundation
// - represents a single Task
struct TaskItem: Codable {
var isToggledOn: Bool
var title: String
var notes: String
}
// - handles on-device memory retrieval and storage
class MemoryManager {
static var tasks: [TaskItem]! // - static array of TaskItems that currently exists on the device
private let defaults = UserDefaults.standard // - reference to application's UserDefaults dictionary
private let DEFAULTS_KEY = "TASK_LIST" // - the key we use to retrieve/save our array of TaskItems
init() {
MemoryManager.tasks = [TaskItem]()
retrieveData()
saveData()
}
// - decode our array from memory
private func retrieveData() {
// - check if an array of TaskItems already exists in memory
var didFail = false
if let data = UserDefaults.standard.value(forKey: DEFAULTS_KEY) as? Data {
if let tasks = try? PropertyListDecoder().decode(Array<TaskItem>.self, from: data) {
MemoryManager.tasks = tasks
} else { didFail = true }
} else { didFail = true }
// - guard statement: if we had a failure then continue
guard didFail else { return }
// - we had a failure in finding a pre-existing array, create a new array of TaskItems!
MemoryManager.tasks = [
TaskItem(isToggledOn: false, title: "task 1", notes: "this is task 1"),
TaskItem(isToggledOn: false, title: "task 2", notes: "this is task 2"),
TaskItem(isToggledOn: true, title: "task 3", notes: "this is task 3"),
TaskItem(isToggledOn: false, title: "task 4", notes: "this is task 4"),
TaskItem(isToggledOn: false, title: "task 5", notes: "this is task 5"),
TaskItem(isToggledOn: true, title: "task 6", notes: "this is task 6")
]
}
// - encode our array into memory
private func saveData() {
UserDefaults.standard.set(try? PropertyListEncoder().encode(MemoryManager.tasks), forKey: DEFAULTS_KEY)
}
}
此文件演示了許多實用性功能。
在頂部,有一個標題為TaskItem的struct,符合Codable;這種一致性允許我們通過序列化格式(如JSON)對該結構進行編碼/解碼。
之後可以發現,在函數retrieveData()中,使用了guard語句和if let語句檢查UserDefault是否存在一個預先存在的TaskItem數組。
如果不存在這樣的數組,那麼會創建一個包括上述項目的新數組。
在該文件底部,可看到如何通過PropertyListEncoder、字典鍵和可選的try block塊將現有的Codable項目數組編碼到內存中的演示。
此文件的主要用例發生在應用程式的初始化運行階段。
在此階段,檢查需要存儲的預存在項目數組。如果此數組不存在,那麼可以預先使用項目數組進行填充,然後將其保存到內存中供以後調用。
留言 點讚 關注
我們一起分享AI學習與發展的乾貨
編譯組:梁晶晶、胡昕彤
相關連結:
https://medium.com/better-programming/15-quick-tips-to-improve-your-swift-code-ed390c99afcd
如需轉載,請後臺留言,遵守轉載規範