Swift 中错误处理

assert 断言

只在debug 模式下程序会自动退出

1
public func assert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

assertionFailure 断言错误

只在debug 模式下程序会自动退出, 没有条件

1
public func assertionFailure(_ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

precondition

releas 模式程序也会退出

1
public func precondition(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = default, file: StaticString = #file, line: UInt = #line)

throw

Swift 泛型

泛型定义–wikipedia

  1. 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
  2. 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(现在人们大多把这称作模板)

泛型的用处

泛型可以实现传入一个指定类型后做一些操作然后返回后还是原来指定的类型,你可以指定特定的类型(特定是指约束)

泛型和 Any 的区别

区别:Any能代表任何类型,但是不能返回某个具体的类型,都需要使用 as 去转化,而泛型正好相反

  • Any 可以代表任何类型,除了class之外还可以代表struct,enum,
  • AnyObject 可以代码任何 class 的类型, swift 中基本类型, ArrayDictionary 都是 struct 类型,都不能使用 AnyObject 来接受

Swift中Any 和 AnyObject用法

泛型函数和泛型参数

swapTwoValues是一个泛型函数,可以接收或者返回任何类型

1
2
3
4
5
func swapTwoValues<T>(inout a: T, inout b: T) {
let temporaryA = a
a = b
b = temporaryA
}
  • swapTwoValues 函数名后面的 <T> 表示这个 T 类型是函数所定义的一个类型, T 只是一个类型的占位符,不用关心具体类型

  • (inout a: T, inout b: T) 中a和b两个参数类型T是泛型参数

类型约束

两个泛型参数的泛型函数

1
2
3
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
  • 泛型参数T约束条件:必须是SomeClass类的子类
  • 泛型参数U约束条件:必须遵守SomeProtocol协议
  • 使用where指定约束

Swift协议和别名

协议

某些属性的集合,可以有多个协议组成,具有的特点

  1. 可以声明函数,但是不能实现,由实现这协议的具体结构或者类来实现
  2. 声明的函数如果有参数也不能设置参数的默认值, 默认参数被认为也是函的一种实现
  3. 可以声明属性,必须是计算型属性 只能使用var 修饰,不能是存储型属,必须在 {} 中明确指出属性的读取类型 例如 {get set}
  4. 遵守协议的类可选择存储型属性或者计算型属性来实现协议中定义的属性
  5. protocol Person: class { } 表示此协议只能被类来遵守,结构体能遵守
  6. 协议可以看着是一种类型,如果协议中的属性是只读的,如果是协议类型只能访问get 方法, 但是遵守协议的具体类或者结构可以设置属性的读和写

  7. 协议中的构造函数

    - 如果父类中遵守了协议中的构造函数,子类中必须重写父类中的构造函数并且使用require修饰构造函数,表示继承此类的子类也必须实现该构造函数
    - 如果class 使用了final 修饰,表示不能被继承,此时不需要 require 关键词修饰
    

类型别名

  • 定义别名 typealias, 供扩展使用

    1
    2
    3
    4
    5
    6
    7
    8
    typealias Length = Double
    extension Double {
    var km: Length {return self * 1_000.0}
    var m: Length {return self * 100.0}
    var cm: Length {return self / 10.0}
    /// 英尺
    var ft: Length { return self / 3.28084}
    }
使用

1
let distance: Length = 10.5.km
  • 定义别名防止硬编码

    1
    2
    	// 音频采样率
    typealias AudioSample = UInt64

协议中 typealias

协议中定义一个属性,该属性的类型根据不同的情况返回不同的类型(我理解为类似泛型),具体的类型由遵守此协议的类或者结构指定

  • 定义协议中别名
1
2
3
4
5
protocol WeightCalculable {
// 属性重量根据不同体积返回的可能是Int,可以可能是double类型,所以使用别名, 协议中使用associatedtype关键词,
associatedtype WeightType
var weight: WeightType { get }
}

扩展 Int 中的吨单位

1
2
3
4
extension Int {
typealias Weight = Int
var t: Weight {return self * 1_000}
}
  • 使用协议中别名
  • 一部手机的时使用该协议WeightCalculable
1
2
3
4
5
6
class iPhone: WeightCalculable {
typealias WeightType = Double
var weight: WeightType {
return 0.5
}
}
  • 一辆大卡时使用该协议WeightCalculable
1
2
3
4
5
6
7
8
class Car: WeightCalculable {
typealias WeightType = Int
let weight: WeightType

init(weight: Int) {
self.weight = weight
}
}
1
let bigCar = Car(weight: 8_000.t) // 8 吨

面向协议编程

  • 定义比赛协议,具有 赢/输/总场次/胜率 等特性
1
2
3
4
5
6
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double
}
  • 协议中方法或者属性的默认实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
extension Recordable {
// CustomStringConvertible 协议默认实现
var description: String {
return String(format: "WINS: %d---LOSSES: %d", [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
return wins + losses
}

// 默认实现的方法
func shoutWins() {
print("come on", wins, "times!")
}
}

篮球比赛:

  • Equatable 协议 - 相等比较 需要重载 ==
  • Comparable 协议 - 是否可比较 ,重载 < 可直接调用sort()函数
  • CustomStringConvertible 协议 - 打印信息,需重写 description
  • Recordable协议 - 比赛协议,没有平局
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
struct BasketballRecord:Equatable, Comparable, CustomStringConvertible, Recordable {
var wins: Int
var losses: Int


/// 在具体定义的类型中实现 协议中的属性或者方法就会覆盖协议中的
var totalGames: Int = 200

var description: String {
return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

// 胜率
func winningPerent() -> Double {
return (Double(wins) / Double(totalGames))
}

// swift4.1 后,不需要再重写此方法,swift 会自动合成,如果不想让某一个属性参与比较,就需要重写该方法
static func == (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
return lhs.wins == rhs.wins && lhs.losses == rhs.losses
}

// Comparable
static func < (lhs: BasketballRecord, rhs: BasketballRecord) -> Bool {
if lhs.wins != rhs.wins {
return lhs.wins < rhs.wins
}
return lhs.losses > rhs.losses
}
}
```

#### 足球比赛:

有平局的情况,原来的协议不能满足,需要增加一个平局的协议

- 定义平局协议

protocol Tieable {
/// 平局,可读写
var ties: Int { get set }

}

1
2
3
4

- 足球比赛需要遵守的协议 `Recordable` 和 `Tieable`

- 总场数需要加上平局的场数,需要实现totalGames的get方法

struct FootableRecord: Recordable, Tieable {
var wins: Int
var losses: Int
// 平局数
var ties: Int

// 总场数
var totalGames: Int {
    return wins + losses + ties
}

var description: String {
    return "本次比赛胜: " + String(wins) + "负" + "\(losses)"
}

func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
}

}

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

- 足球比赛中以上写法存在的问题

- 所有有平局的比赛计算总场次的逻辑都是一样,每次都要写一次实现
- 单一修改 Recordable 协议不能解决问题,
- 存在平局和没有平局两种情况 totalGames 的计算逻辑不相同

- 解决办法:
- 扩展遵守了`Recordable`协议的类型,前提条件是:这个类型遵守了 Tieable 同时也遵守了 `Recordable ` 协议,可以理解为如果该类型遵守了 `Tieable `协议之后,`Recordable` 协议中的 totalGames 属性实现是另一种方式了,不在是以前的 `totalGames` 中直接返回 `wins + losses` 的值


定义协议

protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double

}

1
2

协议默认实现

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
    return wins + losses
}

// 默认实现胜率
func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
}

// 默认实现的方法
func shoutWins() {
    print("come on", wins, "times!")
}

}

1
2

扩展遵守了Tieable 的 类型的 Recordable 协议

extension Recordable where Self: Tieable {
var totalGames: Int {
return wins + losses + ties
}
}

1
2
3
4
5
6

### 协议聚合

如果对某一类型需要限制他必须遵守某些协议,可以使用协议聚合来定义

比如: 有个奖赏协议

protocol Prizable {
func isPrizable() -> Bool
}

1
2

篮球比赛遵守此协议

// 篮球比赛奖赏
extension BasketballRecord: Prizable {
func isPrizable() -> Bool {
return totalGames > 10 && winningPerent() > 0.5
}
}

1
2

足球比赛奖赏遵守此协议

extension FootableRecord: Prizable {
func isPrizable() -> Bool {
return wins > 1
}
}

1
2

现在有某一个学生也遵守了此协议

struct Student: Prizable {
var name: String
var score: Int

func isPrizable() -> Bool {
    return score >= 60
}

}

1
2

定义奖赏的方法,参数类型必须是遵守了此协议的结构或者类型

private func award(_ one: Prizable) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2

如果说 BasketballRecord 这个类还遵守了其他的协议,例如遵守了 `Recordable` 协议, 并且这个协议也遵守了 `CustomStringConvertible` 并且默认实现了`description` 的`get` 方法

// MARK: - 比赛协议,具有 赢。输。总场次。胜率 特性
protocol Recordable: CustomStringConvertible {
var wins: Int { get }
var losses: Int { get }

func winningPerent() -> Double

}

1
2

`Recordable` 默认实现

extension Recordable {
var description: String {
return String(format: “WINS: %d—LOSSES: %d”, [wins, losses])
}

// 默认实现总场数计算型属性
var totalGames: Int {
    return wins + losses
}

// 默认实现胜率
func winningPerent() -> Double {
    return (Double(wins) / Double(totalGames))
}

// 默认实现的方法
func shoutWins() {
    print("come on", wins, "times!")
}

}

1
2
3
4
5

此时如果 `Student` 类还是调用 `award` 方法的话,print(one) 打印的信息将是`Recordable `中默认实现的内容,因此需要约束`award`函数的参数必须遵守两个协议让`Student` 也重写自己的`description `属性的`get`方法,不能再让 `Prizable` 扩展 默认实现

- swift 3 写法: protocol<A, B>
- swift 4 写法: A & B

private func award2(_ one: Prizable & CustomStringConvertible) {
if one.isPrizable() {
print(one)
print(“恭喜获得奖励”)
} else {
print(one)
print(“很遗憾”)
}
}

1
2
3
4
5
6
7
8
9
10

### 泛型约束


- 定义一个函数,找出一个学生数组中分数最大的
- 参数:一个学生数组,都遵守了 Comparable 的类型
- 返回值:某个遵守了 Comparable 的类型实例
- 此时函数报错 `Protocol 'Comparable' can only be used as a generic constraint because it has Self or associated type requirements`,

因为 Comparable 协议中定义的方法 public static func < (lhs: Self, rhs: Self) -> Bool 的参数类型是Self,是具体的某个类型

func maxScore(seq: [Comparable]) -> Comparable { }

1
2
3
4
5


- 如果需要定义一个函数实现在一个数组中找出需要奖励的人的名字该如何实现呢
- 参数:遵守两个协议 Comparable 和 Prizable 协议, 并且使用泛型
- 返回值:返回值是可选值,有可能没有任何奖励的对象

func topPrizable<T: Comparable & Prizable>(seq: [T]) -> T? {
return seq.reduce(nil) { (tempTop: T?, condender: T) in
guard condender.isPrizable() else { return tempTop }
// 解包 condender 失败, 上一层验证了他必须是奖励的那个
guard let tempTop = tempTop else { return condender }
return max(tempTop, condender)
}
}

`

Swift协议扩展

定义结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Point {
var x = 0.0
var y = 0.0

}

struct Size {
var height = 0.0
var width = 0.0
}

struct Rectangle {
var origin = Point()
var size = Size()

init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
}

扩展

  • 扩展方法
1
2
3
4
5
6
extension Rectangle {
mutating func translate(x: Double, y: Double) {
self.origin.x += x
self.origin.y += y
}
}
  • 只能扩展计算型的属性,不能扩展存储型属性, 存储型属性需要在定义类或者结构体时声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension Rectangle {
var center: Point {
get {
let center_x = origin.x + size.width / 2.0
let center_y = origin.y + size.height / 2.0
return Point(x: center_x, y: center_y)
}

set {
origin.x = newValue.x - size.width / 2.0
origin.y = newValue.y - size.height / 2.0
}
}
}
  • 扩展构造方法

    • 类中不能扩展指定构造方法,只能在结构体中扩展
    • 结构体中不能扩展便利构造方法m,只能在类中扩展
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

extension Rectangle {
init(center: Point, size: Size) {
let origin_x = center.x - size.width / 2.0
let origin_y = center.y - size.height / 2.0
self.origin = Point(x: origin_x, y: origin_y)
self.size = size
}

// 结构体中不能扩展便利构造函数 Delegating initializers in structs are not marked with 'convenience'
// convenience init(center: Point, size: Size) {
// let origin_x = center.x - size.width / 2.0
// let origin_y = center.y - size.height / 2.0
// self.origin = Point(x: origin_x, y: origin_y)
// self.size = size
// }
}
  • 扩展嵌套类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extension Rectangle {
enum Vertex: Int {
case left_top
case left_bottom
case right_bottom
case right_top
}

// 获取某一个顶点坐标
func point(of vertex: Vertex) -> Point {
switch vertex {
case .left_top:
return origin
case .left_bottom:
return Point(x: origin.x, y: origin.y + size.height)
case .right_bottom:
return Point(x: origin.x + size.width, y: origin.y + size.height)
case .right_top:
return Point(x: origin.x + size.width, y: origin.y)
}
}
}
  • 扩展下标,根据传入的索引获取对应顶点坐标
1
2
3
4
5
6
extension Rectangle {
subscript(index: Int) -> Point? {
assert(0 <= index && index < 4, "传入值非法")
return point(of: Vertex(rawValue: index)!)
}
}

扩展系统方法

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
extension Int {
/// 平方
var square: Int {
return self * self
}

/// 立方
var cube: Int {
return self * self * self
}

/// 判断数组是否在某一个范围内
func inRange(clousedLeft left: Int, openRight right: Int) -> Bool {
return self >= left && self > right
}

/// 重复执行操作
func repeatitions(task: () -> ()) {
for _ in 0..<self {
task()
}
}

/// 变长函数
func stride(from: Int, to: Int, by: Int, task: () -> ()) {
// 系统变长函数
for i in Swift.stride(from: 0, to: 21, by: 3) {
print(i)
}

for _ in Swift.stride(from: from, to: to, by: by) {
task()
}
}
}

扩展后使用

1
2
3
4
5
6
7
8
9
10
11
12
print(2.square)
print(3.cube)
print(4.inRange(clousedLeft: 0, openRight: 4))
4.repeatitions {
print("extension")
}


// 使用变长函数
4.stride(from: 0, to: 8, by: 4) {
print("stride")
}

Swift下标和运算符重载

字典数组下标

var array = [1,2,3,4,5,6]
let temp = array[0]     // 通过下标访问
let temp2 = array[2]    // 通过下标访问

结构体下标

  • 对于结构体不是直接使用下标访问,会直接报错
    直接使用下标获取属性值报错: Type 'Vector_3' has no subscript members

  • 可以通过实现 subscript(index: Int) -> Double?方法让结构体支持下标访问,可以理解为一个特殊的函数,需要传入参数和返回值

  • 可以增加第二种下标–根据坐标轴获取属性值
  • 重写 subscript 的 set 方法 可以使用下标修改属性值
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
struct Vector_3 {
var x = 0.0
var y = 0.0
var z = 0.0

// 根据索引获取
subscript(index: Int) -> Double? {

// 重写了get方法 实现通过下标获取属性值
get {
switch index {
case 0: return x
case 1: return y
case 2: return z
default: return nil
}
}

// 重写 set 方法,实现g通过下标设置属性值,使用系统默认的newValue
set {
guard let newValue = newValue else {return}
switch index {
case 0: x = newValue
case 1: y = newValue
case 2: z = newValue
default: return
}
}
}

// 增加第二种下标--根据坐标轴获取属性值
subscript(axis: String) -> Double? {

get {
switch axis {
case "X", "x": return x
case "Y", "y": return y
case "Z", "z": return z
default: return nil
}
}

set {
guard let newValue = newValue else {return}
switch axis {
case "X", "x": x = newValue
case "Y", "y": y = newValue
case "Z", "z": z = newValue
default: return
}
}
}
}

```

### 下标访问

结构体使用下标获取值

var v = Vector_3(x: 1, y: 2, z: 3)
print(v[0], v[1], v[2], v[3], v[100]) // Optional(1.0) Optional(2.0) Optional(3.0) nil nil
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(1.0) Optional(2.0) Optional(3.0) nil

1
2

结构体使用下标修改值

v[0] = 101
v[1] = 202
v[2] = 303
v[3] = 400
v[100] = 51
print(v[0], v[1], v[2], v[3], v[100]) // Optional(101.0) Optional(202.0) Optional(303.0) nil nil

1
 

v[“x”] = 100
v[“y”] = 200
v[“z”] = 300
v[“i”] = 50
print(v[“x”], v[“y”], v[“z”], v[“i”]) // Optional(100.0) Optional(200.0) Optional(300.0) 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
45
46
47
48
49
50
51
52

### 多维下标

定义一个关于矩阵的结构体

struct Matrix {
var data: [[Double]]
let row: Int
let column: Int

init(row: Int, column: Int) {
self.row = row
self.column = column
data = [[Double]]()
for _ in 0..<row {
let aRow = Array(repeating: 1.0, count: column)
data.append(aRow)
}
}

// 通过下标 [row,column] 方式访问
subscript(row: Int, column: Int) -> Double {
get {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
return data[row][column]
}

set {
assert(row >= 0 && row < self.row && column >= 0 && column < self.column, "下标不合法")
data[row][column] = newValue
}
}

// 通过下标 [row][column] 方式访问
subscript(row: Int) -> [Double] {
get {
assert(row >= 0 && row < self.row, "下标不合法")
// 直接返回数组,数组本身有下标
return data[row]
}

set {
assert(newValue.count == column, "下标不合法")
data[row] = newValue
}
}
}


### 运算符重载

- 重载 + 运算符

func +(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one[0]! + other[0]!, y: one[1]! + other[1]!, z: one[2]! + other[2]!)

// return Vector_3(x: one.x + other.x, y: one.y + other.y, z: one.z + other.z)
}

1
2

- 两个参数时相减

func -(one: Vector_3, other: Vector_3) -> Vector_3 {
return Vector_3(x: one.x - other.x, y: one.y - other.y, z: one.z - other.z)
}

1
- 一个参数时去反, 需要 prefix 修饰

prefix func -(a: Vector_3) -> Vector_3 {
return Vector_3(x: -a.x, y: -a.y, z: -a.z)
}

1
2

- 向量相乘/向量和常量相乘

func (one: Vector_3, other: Vector_3) -> Double {
return (one.x
other.x) + (one.y other.y) + (one.z other.z)
}

1
2

- 两个参数不能交换,需要重载两次 `*`

func (one: Vector_3, a: Double) -> Vector_3 {
return Vector_3(x: a
one.x, y: a one.y, z: a one.z)
}

func (a: Double, one: Vector_3) -> Vector_3 {
return one
a

// 也可采用下面写法

// return Vector_3(x: a one.x, y: a one.y, z: a * one.z)
}

1
2

- 修改自身参数,不需要返回值

func +=(one: inout Vector_3, other: Vector_3) {
// 已经重载过 + 运算符,可以直接调用
one = one + other
}

func ==(one: Vector_3, other: Vector_3) -> Bool {
return one.x == other.x &&
one.y == other.y &&
one.z == other.z
}

func !=(one: Vector_3, other: Vector_3) -> Bool {
return !(one == other)

// 也可采用下面写法
return one.x != other.x ||
        one.y != other.y ||
        one.z != other.z

}

func <(one: Vector_3, other: Vector_3) -> Bool {
if one.x != other.x {return one.x < other.x}
if one.y != other.y {return one.y < other.y}
if one.z != other.z {return one.z < other.z}
return false
}

func <=(one: Vector_3, other: Vector_3) -> Bool {
return one < other || one == other

// 也可采用下面写法
return one.x > other.x &&
        one.y > other.x &&
        one.z > other.z

}

func >(one: Vector_3, other: Vector_3) -> Bool {
return (one <= other)
}

1
2
3
4
5
6

### 自定义操作符

`postfix` 声明前后缀关键词, `operator ` 操作符关键词

- a+++

声明后置操作符
postfix operator +++
postfix func +++(vector: inout Vector_3) -> Vector_3 {
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return vector
}

1
2

- +++a

// 声明前置操作符
prefix operator +++
prefix func +++(vector: inout Vector_3) -> Vector_3 {
let temp = vector
vector += Vector_3(x: 1.0, y: 1.0, z: 1.0)
return temp
}

`

Swift 协议使用

对UIView 扩展

按钮/文本框抖动动画

  • 声明协议

    • 协议中定义属性:遵循该协议的类型都具有此属性

      • 必须明确规定该属性是可读的 {get} 或者可写的 {set},或是可读可写的 {get set}
      • 使用static修饰声明一个类类型属性
    • 协议中定义方法

1
2
3
4
5
6
7
8
9
10
11
12
protocol Shakable {}
extension Shakable where Self: UIView {
func shakeAnimation() {
let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.08
animation.repeatCount = 5
animation.autoreverses = true
animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 4, y: self.center.y))
animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x + 4, y: self.center.y))
layer.add(animation, forKey: "position")
}
}
  • 自定义UI控件并遵守协议
1
2
3
class MyButton: UIButton, Shakable {  }
class MySwitch: UISwitch, Shakable { }
class MyTextField: UITextField, Shakable { }
  • 使用
1
2
3
4
5
6
7
8
9
10
let mySwitch = MySwitch()
mySwitch.isOn = true
self.mySwitch = mySwitch
view.addSubview(mySwitch)
mySwitch.translatesAutoresizingMaskIntoConstraints = false
mySwitch.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
mySwitch.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true

// 调用
mySwitch.shakeAnimation()

UITableViewCell使用

定义协议

1
2
3
4
5
6
7
8
9
10
11
protocol ReusableView: class {	}

extension ReusableView where Self: UIView {
static var reuseIdentifier: String {
return String(describing: self)
}
}

```

### 对tableView扩展

extension UITableView {
/// 注册cell
func register<T: UITableViewCell>(T: T.Type) where T: ReusableView {
print(T.reuseIdentifier)
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}

/// 调用cell
func dequeueReusableCell<T: UITableViewCell>(_ indexPath: IndexPath) -> T where T: ReusableView {
    print(T.reuseIdentifier)
    return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
}

}

1
2
3
4

### 用法

- 创建自定义的cell,需要遵守协议

class TableProtocolCell: UITableViewCell, ReusableView {
override var reuseIdentifier: String? {
return “cell”
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .cyan
self.textLabel?.text = “通过协议和泛型创建的cell”
self.textLabel?.textColor = .blue
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

class TableProtocolCell2: UITableViewCell, ReusableView {
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.backgroundColor = .red
self.textLabel?.text = “cell1 - 通过协议和泛型创建的”
self.textLabel?.textAlignment = .right
}
required init?(coder aDecoder: NSCoder) {
fatalError(“init(coder:) has not been implemented”)
}
}

1
2

- 注册和使用

class TableViewProtocolController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(T: TableProtocolCell.self)
tableView.register(T: TableProtocolCell2.self)
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 10
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var cell = UITableViewCell()
    if indexPath.row % 2 == 0 {
        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell
    } else {
        cell = tableView.dequeueReusableCell(indexPath) as TableProtocolCell2
    }
    return cell
}

}

`

Swift 学习

枚举

1. 定义 rawValue 为 Int 类型,初始值为1,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Month: Int {
case January = 1
case February
case March
case April
case May
case June
case July
case August
case September
case October
case November
case December
}

2. rawValue 的整型值可以不连续

1
2
3
4
5
6
enum Season: Int {
case Spring = 1
case Summer = 5
case Autumn = 10
case Winter = 40
}

3. 枚举可以是字符串,字符串是变量名rawValue 的值相等

1
2
3
4
5
6
7
enum ProgrammerLanguae: String {
case Swift
case OC = "Objective-C"
case C = "语言"
case RN = "React-Native"
case Java
}

使用rawValue

1
2
3
func residueNewYear(month: Month) -> Int {
return 12 - month.rawValue
}

调用枚举的Moth的构造函数生成一个Month

1
2
3
4
let month = Month(rawValue: 5)
let swift = ProgrammerLanguae.Swift.rawValue
let oc = ProgrammerLanguae.OC
let RN = ProgrammerLanguae.RN.rawValue

4. 枚举关联值 associate value

1
2
3
4
5
6
7
8
9
10
11
12
enum Status {
case success(Int)
case fail(String)
case null // 未关联值
}

func associateValueTest(isSuccess: Bool) -> Status {
if isSuccess {
return .success(200)
}
return .fail("失败")
}

5. 枚举associateValue多个值

  • 本质是关联了一个元祖(value0, value1, value2…)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Shape {
case Square(width: Double)
case Reactangle(width: Double, height: Double)
case Circle(x: Double, y: Double, radius: Double)
case Point
}

let square = Shape.Square(width: 20)
let reactangle = Shape.Reactangle(width: 10, height: 20)
let circle = Shape.Circle(x: 10, y: 10, radius: 20)
let point = Shape.Point

private func area(shape: Shape) -> Double {
switch shape {
case let .Square(width):
return width * width
case let .Reactangle(width, height):
return width * height
case let .Circle(_, _, radius):
return radius * radius * Double.pi
case .Point:
return 0
}
}

6. 递归枚举

  • 定义一个递归算术表达式
  • 使用 indirect 来修饰
1
2
3
4
5
6
7
8
indirect enum ArithmeticalExpression {
case Number(Int)
case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式

// indirect case Addition(ArithmeticalExpression, ArithmeticalExpression) // + 时两边也是一个表达式
// indirect case Multiplication(ArithmeticalExpression, ArithmeticalExpression) // * 时两边也是一个表达式
}
  • 递归表达式使用 (2+3) * 4
1
2
3
4
let two = ArithmeticalExpression.Number(2)
let one = ArithmeticalExpression.Number(3)
let sum = ArithmeticalExpression.Addition(two, one)
let indirectEnumResult = ArithmeticalExpression.Multiplication(sum, ArithmeticalExpression.Number(4))
  • 计算表达式值的函数
1
2
3
4
5
6
7
8
9
10
private func calculate(expression: ArithmeticalExpression) -> Int {
switch expression {
case let .Number(value):
return value
case let .Addition(left, right):
return calculate(expression: left) + calculate(expression: right)
case let .Multiplication(left, right):
return calculate(expression: left) * calculate(expression: right)
}
}

结构体

  • 结构体中的属性值没有初始化时必须使用构造函数来初始化,否则报错
1
2
3
4
5
6
7
struct Location {
var latitude: Double
var longitude: Double
var placeName: String?
}

let location1 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "测试")
  • 结构体中属性如果都赋了初始值就可以直接初始化
1
2
3
4
5
6
struct Location2 {
var latitude: Double = 0
var longitude: Double = 0
}

let location2 = Location2()
  • 如果未指定初始值,swift 就不会默认初始化为(不初始化)
  • 当属性值为可选值时此时允许值为nil,那么就允许不通过构造函数来初始化赋值
  • let 只有一次赋值机会
  • 不管是类还是结构体都应该提供一个全参数的构造函数 init(latitude: Double, longitude: Double, placeName: String?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Location3 {
let latitude: Double
let longtitude: Double
var placeName: String?

init(coordinateString: String) {
let index = coordinateString.index(of: ",")
let index1 = coordinateString.index(after: index!)
let test_1 = coordinateString.prefix(upTo: index!)
let test_2 = coordinateString.suffix(from: index1)

latitude = Double(test_1)!
longtitude = Double(test_2)!
}

init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longtitude = longitude
self.placeName = placeName
}
}

let test3 = Location3(coordinateString: "12,45")

属性和方法

计算型属性

类型属性

类型方法

属性管擦器

延迟属性

访问控制

单利模式

继承和构造函数

swift 中继承

多态性

属性和函数重载

子类两段式构造

  • 子类构造函数分为两段,第一段是构造自己,第二段是构造父类

    • 父类中构造函数
    • 子类要设置一个自己的构造函数
    • 子类构造函数中需要先初始化自己的属性
    • 然后在构造函数中调用父类初始化父类构造函数中的相关属性

      父类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      class Father {
      var name: String
      var sex: Int = 0
      var old: Int = 32
      var desc: String {
      return "我是\(name)"
      }
      init(name: String) {
      self.name = name
      }

      func run() {
      print("Father can Running")
      }
      }
子类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Son: Father {

var isSwimming = true

var computer: String

override var desc: String {
return "Son is \(name)"
}

init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}
}
  • 子类两段构造都完成后子类的初始化才完成,可以使用self调用属性或者方法

    1
    2
    3
    4
    5
    init(name: String, computer: String) {
    self.computer = computer
    self.getToys() // 此代码报错,子类初始化未完成
    super.init(name: name)
    }

便利构造函数和指定构造函数

  • 便利构造函数只能调用自己的指定构造函数
  • 指定构造函数通过一系列的调用都会调用super.init()
  • 便利构造函数无法调用super.init()
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
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}
init(name: String) {
self.name = name
}

init(name: String, old: Int) {
self.name = name
self.old = old
}

func run() {
print("Father can Running")
}
}

class Son: Father {
var isSwimming = true
var computer: String

override var desc: String {
return "Son is \(name)"
}

// 子类指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
super.init(name: name)
}

// 子类便利构造函数,调用了指定构造函数
convenience override init(name: String) {
let computer = "iMac"
self.init(name: name, computer: computer)
}
}

构造函数继承

  • 子类有可能会继承父类的构造函数
  • 子类没有实现父类的任何指定构造函数;则自动继承父类的所有指定构造函数, 因为继承了指定构造函数所以同时便利构造函数也被继承

父类

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
class Father {
var name: String
var sex: Int = 0
var old: Int = 32
var desc: String {
return "我是\(name)"
}

/// Father指定构造函数-1
init(name: String) {
self.name = name
}

/// Father指定构造函数-2
init(name: String, old: Int) {
self.name = name
self.old = old
}

/// Father便利构造函数
convenience init(old: Int) {
self.init(name: "Father", old: old)
}

func run() {
print("Father can Running")
}
}

子类

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
class Son: Father {
var isSwimming = true
var computer: String
var job: String

override var desc: String {
return "Son is \(name)"
}

/// 子类重载的指定构造函数-1
convenience override init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类重载的指定构造函数-2
override convenience init(name: String, old: Int) {
self.init(name: name, computer: "acer")
}


/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}

// 子类便利构造函数,调用了自己指定构造函数
convenience init(computer: String) {
let name = "小张"
self.init(name: name, computer: computer)
}
}

未实现父类任何的指定构造函数

1
2
3
class Grandson: Son {
var toy: String = "dog toys"
}

子类可以调用的构造函数

1
2
3
4
5
let grandSon0 = Grandson(old: 4)
let grandSon1 = Grandson(computer: "Mi")
let grandSon2 = Grandson(name: "小王")
let grandSon3 = Grandson(name: "小虎", computer: "👽")
let grandSon4 = Grandson(name: "小李", old: 8)
  • 子类实现了父类所有的指定构造函数,则自动继承父类的所有便利构造函数
1
2
3
4
5
let son0 = Son(old: 30)
let son1 = Son(computer: "Mi")
let son2 = Son(name: "小王")
let son3 = Son(name: "小虎", computer: "👽")
let son4 = Son(name: "小李", old: 8)

required 构造函数

  • 父类中有被 required 关键词修饰的构造函数子类必须实现此构造函数
  • 子类实现的此构造函数不需要再使用 override 关键词修饰,需要 required 修饰

子类

1
2
3
4
5
6
7
8
class Father {
var name: String
/// Father指定构造函数-1
// required 修饰的指定构造函数子类中必须实现
required init(name: String) {
self.name = name
}
}

父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son: Father {
var isSwimming = true
var computer: String

/// 子类重载的指定构造函数-1
convenience required init(name: String) {
self.init(name: name, computer: "Dell")
}

/// 子类自己的指定构造函数,调用了父类的指定构造函数
init(name: String, computer: String) {
self.computer = computer
self.job = "C#"
super.init(name: name)
}
}
  • 子类如果实现了自己的指定构造函数,那么 required 修饰指定构造函数就初始化失败,因为自己实现了指定构造函数所以不能继承父类中的构造函数,此时可以自己实现被required 修饰的便利构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Son1: Son {
var sport: String

// 指定构造函数,需要调用父类的指定构造函数
init(name: String, sport: String) {
self.sport = sport
Son.init(name: name)
}

// 父类中有被 required 关键词修饰的必须实现的构造函数
convenience required init(name: String) {
// fatalError("init(name:) has not been implemented")
// 调用自己的指定构造函数,
self.init(name: name, sport: "")
}
}

RN学习中遇到的错误总结

执行 npm install 时报错

错误1. Unexpected end of JSON input while parsing near '...er":"0.4.0"},"bin":{"'

本地有package.json文件,执行 npm install 报错,或者clone 的项目执行npm install 也报同样的错误, 可能是因为缓存导致,

解决办法:

  1. 设置镜像源,以前可能大部分都是用的淘宝

    1
    npm set registry https://registry.npmjs.org/
  2. 清除缓存

    1
    npm cache clean –-force
  3. 如果提示失败,使用sudo

    1
    sudo npm cache clean --force

错误2. xcrun: error: unable to find utility "simctl", not a developer tool or in PATH

可能是因为 Xcode 版本问题引起,XCode 偏好设置中 Command line Tools 中为选择版本问题导致(我的情况是安装了Xcode 10 beta版导致的)

解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
The following build commands failed:
CompileC /Users/liepin/LiePinWorkspace/RNDemo/GitHubPopular/ios/build/Build/Intermediates.noindex/RCTWebSocket.build/Debug-iphonesimulator/RCTWebSocket.build/Objects-normal/x86_64/RCTSRWebSocket.o RCTSRWebSocket.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
Installing build/Build/Products/Debug-iphonesimulator/GitHubPopular.app
An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):
Failed to install the requested application
An application bundle was not found at the provided path.
Provide a valid path to the desired application bundle.
Print: Entry, ":CFBundleIdentifier", Does Not Exist

Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/GitHubPopular.app/Info.plist
Print: Entry, ":CFBundleIdentifier", Does Not Exist

错误3. Error: Cannot find module '../lib/utils/unsupported.js'

安装的node 版本不是稳定的版本,需要删除后重新安装

1
2
sudo rm -rf /usr/local/lib/node_modules/npm
brew reinstall node

错误4. Couldn't find preset "module:metro-react-native-babel-preset" when running jest

解决方法

已有项目集成RN

报错1. 文件路径错误,查看你的node_modules 文件夹是在当前目录还是上级目录

[!] No podspec found for `React` in `../node_modules/react-native
  • ../ 是指父级目录

  • ./ 是指当前目录

报错 2. yoga Y 大小写为问题

[!] The name of the given podspec `yoga` doesn't match the expected one `Yoga`_modules/react-native/ReactCommon/yoga"

pod install 之后

报错 1.

解决办法: pod ‘React’, :subspecs 中加入 ‘CxxBridge’, ‘DevSupport’

pod 'React', :path => './node_modules/react-native', :subspecs => [
'Core',
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'CxxBridge', # Include this for RN >= 0.47
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43

]

报错 2.

RCTAnimation/RCTValueAnimatedNode.h' file not found

解决方法:

1
#import <RCTAnimation/RCTValueAnimatedNode.h>

替换为

1
#import "RCTValueAnimatedNode.h"

修改头文件导入方式

报错3 版本不匹配 react native version mismatch javascript version 0.54.3 native 0.57.2

  • 关闭所有的 terminal,或者是集成开发环境的命令行窗口

    1
    2
    watchman watch-del-all
    react-native start --reset-cache
  • 重新打开一个terminal窗口

    1
    react-native run-ios

    问题描述和解决1

    问题描述和解决2

报错4 Unable to resolve module "schedule/tracking"

缺少一些开发依赖的库

npm i schedule@0.4.0 --save-dev

问题描述和解决

React-Native开发iOS打包

打jsbudnle包

创建打包后存放文件目录

  • cd 到 React-Native 项目根目录下 并且 进入 ios 文件目录下
  • 创建 bundle 文件夹

    1
    mkdir bundle

配置 React-Native 打包命令

  • 打包命令

    1
    react-native bundle
  • 在React Native项目的根目录下执行命令

    1
    react-native bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle
- `--entry-file`: ios或者android入口的js名称,比如 `index.js`

- `--platform`: 平台名称(ios或者android)

- `--dev`: 设置为`false`的时候将会对`JavaScript`代码进行优化处理。

- `--bundle-output`: 生成的jsbundle文件的名称和路径,比如 `./ios/bundle/index.jsbundle`

- `--assets-dest`: 图片以及其他资源存放的目录

使用打包命令

  • 将打包命令添加到 packger.json

    1
    2
    3
    4
    5
    "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start",
    "test": "jest",
    "bundle-ios": "node node_modules/react-native/local-cli/cli.js bundle --entry-file index.js --platform ios --dev false --bundle-output ./ios/bundle/index.jsbundle --assets-dest ./ios/bundle"
    },
  • 再次打包只需要 React Native项目的根目录下执行命令,不用再次输入类似上面的命令

    1
    npm run bundle-ios

集成到Xcode(iOS原生项目中)

  • 添加已经打好的.jsbundle 离线包

  • iOS 项目代码配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    private func loadReactNativeTest() {
    // debug 测试加载本地文件
    let url = URL(string: "http://localhost:8081/index.bundle?platform=ios")!

    // 加载已经加入的离线包
    let url2 = Bundle.main.url(forResource: "index", withExtension: "jsbundle")

    let rootView = RCTRootView(
    bundleURL: url,
    moduleName: "MyApp", //这里的名字要和index.js中相同
    initialProperties: nil,
    launchOptions: nil
    )
    view = rootView
    }