15种快速技巧,提升Swift编码能力

1,817 阅读9分钟
全文共8828字,预计学习时长16分钟
15种快速技巧,提升Swift编码能力

本文介绍的这些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 stacklet 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

}

15种快速技巧,提升Swift编码能力


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中可发现,可以在完成动作(动画、网络调用等)后发送警报,或者发送包含数据的警报。

15种快速技巧,提升Swift编码能力


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项目数组编码到内存中的演示。

此文件的主要用例发生在应用程序的初始化运行阶段。

在此阶段,检查需要存储的预存在项目数组。如果此数组不存在,那么可以预先使用项目数组进行填充,然后将其保存到内存中供以后调用。

15种快速技巧,提升Swift编码能力

留言 点赞 关注

我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”


(添加小编微信:dxsxbb,加入读者圈,一起讨论最新鲜的人工智能科技哦~)