全文共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 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
}

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学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”

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