KM的博客.

Swift备忘录

字数统计: 4.8k阅读时长: 21 min
2018/02/18

SwiftTip

问题记录

  • 柯里化 什么意思

  • POPOOP的区别

  • AnyAnyObject 区别

  • rethrowsthrows 有什么区别呢?

  • break return continue fallthough 在语句中的含义(switch、while、for)

  • 关键字

    • public open final
    • static class
    • mutating inout
    • infix operator
    • dynamicMemberLookup
    • where indirect和嵌套
    • @dynamicCallable
    • @autoclosure
    • @escaping
  • 为什么要用inout修饰函数的参数才能修改参数?

    • 什么时候使用convenience
  • 协议 Protocol

    • ExpressibleByDictionaryLiteral
    • Sequence
    • Collection
    • CustomStringConvertible
  • Hashable Codable

    • Comparable
    • RangeReplaceableCollection

    以上协议常见应用场景是什么,有什么作用?

iOS初始化核心原则

iOS 的初始化最核心两条的规则:

• 必须至少有一个指定初始化器,在指定初始化器里保证所有非可选类型属性都得到正确的初始化(有值)

• 便利初始化器必须调用其他初始化器,使得最后肯定会调用指定初始化器

示例如下:

1
2
3
4
5
6
public override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience init(style: Style) {
self.init(frame: .zero)
}

在Swift中千万不要用String的count方法计算文本长度,否则当文本中有emoji时,会计算出错。

应当转成NSString再去求length

Swift访问控制权限

Swift的访问权限管理依赖于两个概念:module和文件。module是一个完整的代码单元,

它可以是一个或多个框架(Framework),或者是一个App Bundle,可以被import导入到工程中。

文件指的就是Swift File,它通常属于一个module。

Swift 为代码中的实体提供了5种不同的访问级别:open、public、internal、fileprivate、private。

Open 为最高级访问级别,private 为最低级访问级别, internal是module中默认权限,private和fileprivate的区别如下:

访问级别 定义
open 这个属性允许在 app 内或 app 外重写和访问。在开发框架的时候,会应用到这个访问修饰符。
public 可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。
internal 默认权限可以访问自己module中源文件里的任何实体
fileprivate 只能在当前源文件中使用。
private 只允许实体在定义的类以及相同源文件内的 extension 中访问

public 和 open 的区别在于:

  • 只有被 open 标记的内容才能在别的框架中被继承或者重写。

  • 不希望他们继承或者重写的话,应该将其限定为 public

private 和 fileprivate 的区别在于:

  • private 让代码只能在当前作用域或者同一文件中同一类型的作用域中被使用

  • fileprivate 表示代码可以在当前文件中被访问,而不做类型限定。

1、如果希望name仅在当前文件中可访问,可以使用private修饰

1
2
3
class Person {
private var name: String?
}

2 、但是在开发中所面临的更多的情况是我们希望在类型之外也能够读取到这个类型,同时为了保证类型的封装和安全,只能在类型内部对其进行改变和设置。

下面这种写法没有对读取做限制,相当于使用了默认的 internal 权限。

1
2
3
class Person {
private(set) var name: String?
}

3、如果希望外部可以读取,但不可以修改。这也是为了保证类型的封装和安全,在内部提供可读可写,而外部仅仅可读

  • 这种写法相当于把setter设置为private,而getter仍然是默认的internal
1
2
3
class Person {
public private(set) var name: String?
}

如果既想要外部可读,又想仅内部可写,可以为getter加上public

声明关键字

associatedtype:在协议中,定义一个类型的占位符名称。直到协议被实现,该占位符才会被指定具体的类型。
1
2
3
4
5
6
7
8
9
protocol Entertainment  
{
associatedtype MediaType
}

class Foo : Entertainment
{
typealias MediaType = String //可以指定任意类型
}
class:通用、灵活的结构体,是程序的基础组成部分。与 struct 类似,不同之处在于:
  • 允许一个类继承另一个类的特性。
  • 类型转换,允许在运行时检查和指定一个类的实际类型。
  • 析构方法允许类的实例释放所有资源。
  • 引用计数允许多个引用指向一个实例。
struct:通用、灵活的结构体,是程序的基础组成部分,并提供了默认初始化方法。与 class 不同,当 struct 在代码中被传递时,是被拷贝的,并不使用引用计数。除此之外,struct 没有下面的这些功能:
  • 使用继承。
  • 运行时的类型转换。
  • 使用析构方法。
1
2
3
4
5
6
struct Person  
{
var name:String
var age:Int
var gender:String
}
extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。
NSErrorcode、domin、userInfo

Swift Error分类

  • domain error
  • Recoverable error
  • Universal error
  • Logic error
1
2
3
4
enum Result<T> {
case Success(T)
case Failure(NSError)
}
try!表示强制执行,如果发生异常程序crash
try?返回可选项,如果发生异常返回nil 不会crash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
enum E: Error {
case Negative
}

func methodThrowsWhenPassingNegative(number: Int) throws -> Int {
if number < 0 {
throw E.Negative
}
return number
}

if let num = try? methodThrowsWhenPassingNegative(100) {
print(num.dynamicType)
} else {
print("failed")
}

Swift 5.1 初始化和反初始化

enum包含嵌套方法需要使用indirect

否则报错: Recursive enum "" is not marked 'indirect'

indirect和嵌套

1
2
3
4
indirect enum LinkedList<Element: Comparable> {
case empty
case node(Element, LinkedList<Element>)
}

为什么要用inout修饰函数的参数才能修改参数?

  • 这是因为函数的参数一般是值类型,比如Int类型就是值类型,当我们想要修改函数内部参数时,我们是不能直接修改默认let修饰的函数参数Int的,我们需要使用&符号来修饰参数,这样inout在函数内部会创建一个新的值,然后在函数return的时候把值赋值给&修改的变量,这和我们常见的class引用类型是不一样的做法

    1
    2
    3
    4
    5
    6
    func makeIncrement(number: Int) -> ((inout Int) -> ()) {
    func increamor(v: inout Int) -> () {
    v += number
    }
    return increamor(v:)
    }

什么时候使用*convenience?

时间戳

毫秒
1
2
3
let mill = Int64(round(Date().timeIntervalSince1970 * 1000))
let mill = Int64(Date().timeIntervalSince1970 * 1000)

Swift 单例

1
2
3
4
class Manager  {
static let shared = Manager()
private init() {}
}

这种写法不仅简洁,而且保证了单例的独一无二。

在初始化类变量的时候,Apple 将会把这个初始化包装在一次 swift_once_block_invoke 中,以保证它的唯一性。

不仅如此,对于所有的全局变量,Apple 都会在底层使用这个类似 dispatch_once 的方式来确保只以 lazy 的方式初始化一次。

另外,我们在这个类型中加入了一个私有的初始化方法,来覆盖默认的公开初始化方法,这让项目中的其他地方不能够通过 init 来生成自己的 MyManager 实例,也保证了类型单例的唯一性。

如果你需要的是类似 default 的形式的单例 (也就是说这个类的使用者可以创建自己的实例) 的话,可以去掉这个私有的 init 方法。

摘录来自: 王巍 (onevcat). “Swifter - Swift 必备 Tips (第四版)。” Apple Books.

Swift 闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
///定义一个闭包
var itemClickCallback: ((AnalysisSpaceInfo) -> Void)?
var rankViewScrollCallBack: (() -> Void)?

/// 使用
view.itemClickCallback = { [weak self] item in
guard let self = self else { return }
self.loadSpaceDetail(id: item.id)
}
view.rankViewScrollCallBack = { [weak self] in
guard let self = self else { return }
self.view.isHidden = true
}

逃逸闭包

1
2
3
4
5
6
7
8
static func getInfo(successHandler: ((Bool) -> Void)? = nil) {

}

static func getInfo(successHandler:@escaping ((Bool) -> Void)) {

}

Precondition预处理

定义
1
2
3
4
5
6
7
8
public func precondition(_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = default,
file: StaticString = #file,
line: UInt = #line)

public func preconditionFailure(_ message: @autoclosure () -> String = default,
file: StaticString = #file,
line: UInt = #line) -> Never
使用
1
precondition(condition: Bool, message: String)
  • precondition 在一般的代码中并不多见,因为它是动态的,只会在程序运行时进行检查,适用于哪些无法在编译期确定的风险情况。

  • 如果出现了诸如数据错误的情况,precondition 会提前终止程序,避免因数据错误造成更多的损失。

    • 如果条件判断为 true,代码运行会继续进行。
    • 如果条件判断为 false,程序将终止。
  • assert 是单纯地触发断言即停止程序,不会让你有机会将可能出错的设计走过它这一关。???

例如:Swift 数组的下标操作可能造成越界,使用扩展的方式向其中增加一个方法来判断下标是否越界。

extension Array {
    func isOutOfBounds(index: Int) {
    precondition((0..<endIndex).contains(index), "数组越界")
    print("继续执行")
    }
}
1
2
3
// 不越界的情况

[1, 2, 3].isOutOfBounds(index: 2) // 继续执行
1
2
3
// 越界的情况

[1, 2, 3].isOutOfBounds(index: 3) // Thread 1: Precondition failed: 数组越界
  • 在满足 precondition 条件的时候,程序会继续执行。
  • 在不满足 precondition 条件的时候,程序被终止,并且将 precondition 中预设的错误信息打印到了控制台上,precondition 避免了一些无意义的操作。

precondition和assert的区别

闭包

@autoclosure作用:将表达式自动封装成一个闭包

()->Void

1.2 ??的底层实现是用的enum

1.3 “闭包和循环引用”

weak解决循环引用的正确写法:

1
2
3
4
5
6
7
8
9
10
11
12
13

var name: ()->() = {

[weak self] in

if let strongSelf = self {

print("The name is (strongSelf.name)")

}

}

值类型和引用类型的选择

  • 数组和字典设计为值类型最大的考虑是为了线程安全.

  • 另一个优点,那就是非常高效,因为 “一旦赋值就不太会变化” 这种使用情景在 Cocoa 框架中是占有绝大多数的,这有效减少了内存的分配和回收。

但是在少数情况下,我们显然也可能会在数组或者字典中存储非常多的东西,并且还要对其中的内容进行添加或者删除。”

  • 在需要处理大量数据并且频繁操作 (增减) 其中元素时,选择 NSMutableArray 和 NSMutableDictionary 会更好,

  • 对于容器内条目小而容器本身数目多的情况,应该使用 Swift 语言内建的 Array 和 Dictionary

@escaping的作用?

1
2
3

class func animate(withDuration duration: TimeInterval, animations: @escaping () -&gt; Void, completion: ((Bool) -> Void)? = nil)

图像 2019-12-5,下午6.41

defer的使用注意点

defer的作用域

以前很单纯地认为 defer 是在函数退出的时候调用,并没有注意其实是当前 scope 退出的时候调用这个事实,造成了这个错误。在 if,guard,for,try 这些语句中使用 defer 时,应该要特别注意这一点。

图像 2019-12-5,下午6.41-1

@discardableResult

Result

Result<T, E: Error> 和 Result

Lazy的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

let data = 1...3

let result = data.lazy.map { (i: Int) -> Int in

print("准备处理(i)")

return i * 2

}

print("准备访问结果")

for i in result {

print("处理后的结果:(i)")

}

print("done")

打印结果:

准备访问结果

准备处理1

处理后的结果:2

准备处理2

处理后的结果:4

准备处理3

处理后的结果:6

done

Swift反射机制Mirror

“通过 Mirror 初始化得到的结果中包含的元素的描述都被集合在 children 属性下,如果你有心可以到 Swift 标准库中查找它的定义,它实际上是一个 Child 的集合,而 Child 则是一对键值的多元组:

示例1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

struct Car {

let logo: String

var wheel: Int

let door: Int

}

let baoM = Car(logo: "BMW", wheel: 4, door: 2)

let mirror = Mirror(reflecting: baoM)

print("类型:(String(describing: mirror.displayStyle))")

///1、通过Mirror的children获取属性信息

print("属性个数:(mirror.children.count)")

mirror.children.map { (child) -> Any in

print("label: (String(describing: child.label)), value: (child.value)")

}

///2、通过Refletion的dump(Any)方法获取属性信息

dump(baoM)

示例2 获取property
1
2
3
4
5
6
7
8
9

let homeProperty = Mirror(reflecting: self)

homeProperty.children.map {

LOG.D("home property:($0)")

}

@propertyWrapper定义与使用

@propertyWrapper定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@propertyWrapper
struct Clamping<Value: Comparable> {
var value: Value
let range: ClosedRange<Value>

init(initialValue value: Value, _ range: ClosedRange<Value>) {
precondition(range.contains(value))
self.value = value
self.range = range
}

var wrappedValue: Value {
get { value }
set { value = min(max(range.lowerBound, newValue), range.upperBound) }
}
}

使用
1
2
3
4
5
6
struct Solution {
@Clamping(0...14) var pH: Double = 7.0
}

let carbonicAcid = Solution(pH: 4.68) // at 1 mM under standard conditions

@propertyWrapper属性包裹器 | SwiftLee

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@propertyWrapper /// 先告诉编译器 下面这个UserDefault是一个属性包裹器
struct UserDefault<T> {
///这里的属性key 和 defaultValue 还有init方法都是实际业务中的业务代码
///我们不需要过多关注
let key: String
let defaultValue: T

init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
/// wrappedValue是@propertyWrapper必须要实现的属性
/// 当操作我们要包裹的属性时 其具体set get方法实际上走的都是wrappedValue 的set get 方法。
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}

struct UserDefaultsConfig {
@UserDefault("has_seen_app_introduction", defaultValue: false)
static var hasSeenAppIntroduction: Bool

@UserDefault("username", defaultValue: "Antoine van der Lee")
static var username: String

@UserDefault("year_of_birth", defaultValue: 1990)
static var yearOfBirth: Int
}


///具体的业务代码。
UserDefaultsConfig.hasSeenAppIntroduction = false
print(UserDefaultsConfig.hasSeenAppIntroduction) // Prints: false
UserDefaultsConfig.hasSeenAppIntroduction = true
print(UserDefaultsConfig.hasSeenAppIntroduction) // Prints: true

Other usage examples

Property wrappers are used throughout the default Swift APIs as well. The new @State and @Binding keys are an example of this. Another idea could be to create a wrapper for thread-specific writing and reading:

1
@ThreadSpecific var localPool: MemoryPool

Or, as some might find handy, a way to define command line actions:

1
2
@Option(shorthand: "m", documentation: "Minimum value", defaultValue: 0)
var minimum: Int
  • A @Positive / @NonNegative property wrapper that provides the unsigned guarantees to signed integer types.
  • A @NonZero property wrapper that ensures that a number value is either greater than or less than 0.
  • @Validated or @Whitelisted / @Blacklisted property wrappers that restrict which values can be assigned.

FirstVC via UIView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extension UIView {

/// Get current view controller based current view.
public func firstViewController() -> UIViewController? {
for view in sequence(first: self.superview, next: { $0?.superview }) {
if let responder = view?.next {
if responder.isKind(of: UIViewController.self) {
return responder as? UIViewController
}
}
}
return nil
}

public func firstResponder() -> UIView? {
var views = [UIView](arrayLiteral: self)
var index = 0
repeat {
let view = views[index]
if view.isFirstResponder {
return view
}
views.append(contentsOf: view.subviews)
index += 1
} while index < views.count
return nil
}
}

TopVC via UIWindow

1
2
3
4
if let controller = UIWindow.topViewController() {
controller.present(alert, animated: true, completion: nil)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
extension UIWindow {

/// Returns the top most controller
open class func topViewController() -> UIViewController? {
let window = UIApplication.shared.delegate?.window
let rootViewController = window??.rootViewController
return topMost(of: rootViewController)
}

/// Returns the top most view controller from given view controller's stack.
open class func topMost(of viewController: UIViewController?) -> UIViewController? {
// presented view controller
if let presentedViewController = viewController?.presentedViewController {
return self.topMost(of: presentedViewController)
}

// UITabBarController
if let tabBarController = viewController as? UITabBarController,
let selectedViewController = tabBarController.selectedViewController {
return self.topMost(of: selectedViewController)
}

// UINavigationController
if let navigationController = viewController as? UINavigationController,
let visibleViewController = navigationController.visibleViewController {
return self.topMost(of: visibleViewController)
}

// UIPageController
if let pageViewController = viewController as? UIPageViewController,
pageViewController.viewControllers?.count == 1 {
return self.topMost(of: pageViewController.viewControllers?.first)
}

// child view controller
for subview in viewController?.view?.subviews ?? [] {
if let childViewController = subview.next as? UIViewController {
return self.topMost(of: childViewController)
}
}
return viewController
}
}

DispatchGroup分组管理异步任务

  • enter和leave必须配对出现,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let group = DispatchGroup()
group.enter()//把该任务添加到组队列中执行
myQueue?.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...10 {
print("耗时任务一")
group.leave()//执行完之后从组队列中移除
}
})

group.enter()//把该任务添加到组队列中执行
myQueue?.async(group: group, qos: .default, flags: [], execute: {
for _ in 0...10 {
print("耗时任务二")
group.leave()//执行完之后从组队列中移除
}
})

//当上面所有的任务执行完之后通知
group.notify(queue: .main) {
print("所有的任务执行完了")
}

Swift4.2中的self

  • 在 4.2 之前,self 是全局保留关键字,所以如果在逃逸闭包中如果在闭包中把 self 标记为 weak 后,如果要使用需要使用 ` 包起来:
1
guard let `self` = self else { return }
  • Swift4.2之后
1
2
3
4
doSomething(then: { [weak self] in
guard let self = self { else return }
self.doSomethingElse()
)
  • 当然取消了这个限制后也意味着 self 可能不一定是 self 了:
1
2
3
4
5
var number: Int? = nil
if let self = number {
print(self) // 这里的 self 是 number:Int
}

给Struct 添加属性

储存属性
1
2
3
4
5
6
7
8
9
10
11
12
///public struct URLRequest : ReferenceConvertible, Equatable, Hashable {}
private var KeyIdentifer: Void?
extension URLRequest {
public internal(set) var key: String? {
get {
return objc_getAssociatedObject(self, &KeyIdentifer) as? String
}
set {
objc_setAssociatedObject(self, &KeyIdentifer, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Bool属性
1
2
3
4
5
6
7
8
9
10
11
12
///public struct URLRequest : ReferenceConvertible, Equatable, Hashable {}
private var KeyIdentifer: Void?
extension xxx {
public internal(set) var isSuccess: Bool? {
get {
return objc_getAssociatedObject(self, &KeyIdentifer) as? Bool
}
set {
objc_setAssociatedObject(self, &KeyIdentifer, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}

typealias使用

顾名思义,typealias 是特定类型的别名。类型,例如 IntDoubleUIViewController 或一种自定义类型。Int32Int8 是不同的类型。换句话说,类型别名在你的代码库里插入现有类型的另一个名称。例如:

1
typealias Money = Int
举例
1
2
3
4
5
6
7
8
9
10
11
struct Bank {
typealias DepositMoney = Int
typealias WithdrawMoney = Int
private var credit: Int = 0
mutating func deposit(amount: DepositMoney) {
credit += amount
}
mutating func withdraw(amount: WithdrawMoney) {
credit -= amount
}
}
提高可读性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final class Dispatcher {
private var successHandler: ((Int) -> Void)?
private var errorHandler: ((Error) -> Void)?

func handle(success: ((Int) -> Void)?, error: ((Error) -> Void)?) {
self.successHandler = success
self.errorHandler = error
internalHandle()
}

func handle(success: ((Int) -> Void)?) {
self.successHandler = success
internalHandle()
}

func handle(error: ((Int)-> Void?)) {
self.errorHandler = error
internalHandle()
}

private func internalHandle() {
...
}
}

该结构体引入了两个闭包,一个用于成功情况,一个用于错误情况。但是,我们还希望提供更方便的函数,调用其中一个处理器即可。在上面的示例中,如果要向成功和错误处理器添加另一个参数(例如 HTTPResponse),那么需要更改很多代码。在三个地方,((Int) -> Void)? 需要变成 ((Int, HTTPResponse) -> Void)?。错误处理器也是一样的。通过使用多个类型别名,可以避免这种情况,只需要在一个地方修改类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
final class Dispatcher {
typealias Success = (Int, HTTPResponse) -> Void
typealias Failure = (Error, HTTPResponse) -> Void

private var successHandler: Success?
private var errorHandler: Failure?

func handle(success: Success?, error: Failure?) {
self.successHandler = success
self.errorHandler = error
internalHandle()
}

func handle(success: Success?) {
self.successHandler = success
internalHandle()
}

func handle(error: Failure?) {
self.errorHandler = error
internalHandle()
}

private func internalHandle() {
...
}
}
组合协议
1
2
3
4
5
6
7
8
protocol CanRead {}
protocol CanWrite {}
protocol CanAuthorize {}
protocol CanCreateUser {}

typealias Administrator = CanRead & CanWrite & CanAuthorize & CanCreateUser
typealias User = CanRead & CanWrite
typealias Consumer = CanRead

在这里,我们定义了权限层。管理员可以做所有事情,用户可以读写,而消费者只能读。

缺点
1
2
func first(action: (Int, Error?) -> Void) {}
func second(action: Success) {}

第二个不是立即就能明白的。**Success 是什么类型?如何构造它?你必须在 Xcode 中按住 Option 单击它,以了解它的功能和工作方式。这会带来额外的工作量。如果使用了许多类型别名,则将花费更多的时间。**这没有很好的解决方案,(通常)只能依赖于用例。

callback
1
2
var select1Callback: ((String) -> Void)?
var select2Callback: ((IndexPath, Model) -> Void)?

处理cell复用

1、prepareForReuse重置

Apple: If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCell(withIdentifier:).

For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view’€™s delegate in tableView(_:cellForRowAt:) should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.

1
2
3
4
5
6
override func prepareForReuse() {
// Clean up
if [self.subviews containsObject: self.someNotoriousView] {
[self.contentView removeFromSuperview];
}
}

2、

Swift不安全的指针

UnsafePointer
UnsafeMutablePointer
UnsafeRawPointer
UnsafeMutableRawPointer
CATALOG
  1. 1. SwiftTip
    1. 1.1. 问题记录
      1. 1.1.1. 关键字
      2. 1.1.2. 协议 Protocol
        1. 1.1.2.1. iOS初始化核心原则
        2. 1.1.2.2. Swift访问控制权限
        3. 1.1.2.3. 声明关键字
          1. 1.1.2.3.1. associatedtype:在协议中,定义一个类型的占位符名称。直到协议被实现,该占位符才会被指定具体的类型。
          2. 1.1.2.3.2. class:通用、灵活的结构体,是程序的基础组成部分。与 struct 类似,不同之处在于:
          3. 1.1.2.3.3. struct:通用、灵活的结构体,是程序的基础组成部分,并提供了默认初始化方法。与 class 不同,当 struct 在代码中被传递时,是被拷贝的,并不使用引用计数。除此之外,struct 没有下面的这些功能:
          4. 1.1.2.3.4. extension:允许给已有的类、结构体、枚举、协议类型,添加新功能。
          5. 1.1.2.3.5. NSError:code、domin、userInfo
          6. 1.1.2.3.6. try!表示强制执行,如果发生异常程序crash
          7. 1.1.2.3.7. try?返回可选项,如果发生异常返回nil 不会crash
        4. 1.1.2.4. Swift 5.1 初始化和反初始化
        5. 1.1.2.5. enum包含嵌套方法需要使用indirect
        6. 1.1.2.6. 为什么要用inout修饰函数的参数才能修改参数?
        7. 1.1.2.7. 什么时候使用*convenience?
        8. 1.1.2.8. 时间戳
          1. 1.1.2.8.1. 毫秒
        9. 1.1.2.9. Swift 单例
        10. 1.1.2.10. Swift 闭包
        11. 1.1.2.11. 逃逸闭包
        12. 1.1.2.12. Precondition预处理
          1. 1.1.2.12.1. 定义
          2. 1.1.2.12.2. 使用
        13. 1.1.2.13. precondition和assert的区别
        14. 1.1.2.14. 闭包
          1. 1.1.2.14.1. @autoclosure作用:将表达式自动封装成一个闭包
        15. 1.1.2.15. 值类型和引用类型的选择
        16. 1.1.2.16. @escaping的作用?
        17. 1.1.2.17. defer的使用注意点
        18. 1.1.2.18. @discardableResult
        19. 1.1.2.19. Result
        20. 1.1.2.20. Lazy的使用
        21. 1.1.2.21. Swift反射机制Mirror
          1. 1.1.2.21.1. 示例1
          2. 1.1.2.21.2. 示例2 获取property
        22. 1.1.2.22. @propertyWrapper定义与使用
          1. 1.1.2.22.1. @propertyWrapper定义
          2. 1.1.2.22.2. 使用
        23. 1.1.2.23. @propertyWrapper属性包裹器 | SwiftLee
          1. 1.1.2.23.1. Other usage examples
          2. 1.1.2.23.2. Related Ideas
        24. 1.1.2.24. FirstVC via UIView
        25. 1.1.2.25. TopVC via UIWindow
        26. 1.1.2.26. DispatchGroup分组管理异步任务
        27. 1.1.2.27. Swift4.2中的self
        28. 1.1.2.28.
        29. 1.1.2.29. 给Struct 添加属性
          1. 1.1.2.29.1. 储存属性
          2. 1.1.2.29.2. Bool属性
        30. 1.1.2.30. typealias使用
          1. 1.1.2.30.1. 举例
          2. 1.1.2.30.2. 提高可读性
          3. 1.1.2.30.3. 组合协议
          4. 1.1.2.30.4. 缺点
          5. 1.1.2.30.5. callback
        31. 1.1.2.31.
        32. 1.1.2.32. 处理cell复用
          1. 1.1.2.32.1. 1、prepareForReuse重置
        33. 1.1.2.33. 2、
        34. 1.1.2.34. Swift不安全的指针
          1. 1.1.2.34.1. UnsafePointer
          2. 1.1.2.34.2. UnsafeMutablePointer
          3. 1.1.2.34.3. UnsafeRawPointer
          4. 1.1.2.34.4. UnsafeMutableRawPointer