Swift语法知识汇总(下)

第十二章 Error处理、泛型

1-1、错误类型

/*开发过程常见的错误:
    语法错误(编译报错)
    逻辑错误
    运行时错误(可能会导致闪退, 一般也叫做异常)
*/

1-2、自定义错误

/*Swift中可以通过Error协议自定义运行时的错误信息.
    枚举、类、结构体都可以定义Error.
*/
///方案一: 普通处理
func divide(_ num1: Int, _ num2: Int) -> Int {
  if num2 == 0 {
    return nil
  }
  return num1 / num2
}
///方案二: 用自定义Error及使用
struct MyError : Error {
  var msg: String
}

func divide(_ num1: Int, _ num2: Int) throws -> Int {//throws表示这个函数有可能会抛出错误
  if num2 == 0 {
    throw MyError(msg: "0不能作为除数")
  }
  return num1 / num2
}
divide(20, 0)//报错

///方案三: 自定义Error处理
enum SomeError : Error {
  case illegalArg(String)
  case outOfBounds(Int, Int)
  case outOfMemory
}
//函数内部通过throw抛出自定义Error, 可能会抛出Error的函数必须加上throws声明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
  if num2 == 0 {
    throw SomeError.illegalArg("0不能作为除数")
  }
  return num1 / num2
}
//需要使用try调用可能会抛出Error的函数
var result = try divide(1, 0)
print(result)

/* do-catch */
/// 可以使用do-catch捕捉Error
func test() {
  print("1")
  do { //异常的代码发到do里面去
    print("2")
    print(try divide(20, 0))///一旦抛出Error后, try下一句直到作用域结束的代码都将停止运行
    print("3")
  } catch let SomeError.illegalArg(msg) {
    print("参数异常:", msg)
  } catch let SomeError.outOfBounds(size, index) {
    print("下标越界:","size=\(size)","index=\(index)")
  } catch SomeError.outOfMemory {
    print("内存溢出")
  } catch {//catch写捕获异常的代码
    print("其他错误")
  }
  print("4")
}

test()
// 1
// 2
// 参数异常: 0 不能作为除数
// 4

1-3、处理Error

/*处理Error的2种方式
    ① 通过do-catch捕捉Error
    ② 不捕捉Error, 在当前函数增加throws声明, Error将自动抛给上层函数
如果最顶层函数(main函数)依然没有捕捉Error, 那么程序将终止
*/
func test() throws {
  print("1")
  print(try divide(20, 0))
  print("2")
}
try test()
// 1
// Fatal error: Error raised at top level

do {
  print(try divide(20, 0))
} catch is SomeError {//判断如果是SomeError类型, 就执行这里
  print("SomeError")
}

do {
  print(try divide(20, 0))
} catch let error {
  switch error {
  case let SomeError.illegalArg(msg):
    print("参数错误: ", msg)
  default: 
    print("其他错误")
  }
}

// ②
func test() throws {
  print("1")
  do {
    print("2")
    print(try divide(20, 0))
    print("3")
  } catch let error as SomeError {
    print(error)
  }
  print("4")
}
try test()
// 1
// 2
// illegalArg("0不能作为除数")
// 4

1-4、try?、try!

/*可以使用try?、try!调用可能会抛出Error的函数, 这样就不用处理Error*/
func test() {
  print("1")
  var result1 = try? divide(20, 10) // Optional(2), Int?
  var result2 = try? divide(20, 0) // nil
  var result3 = try! divide(20, 10) // 2, Int
  print("2")
}
test() 

/*a、b是等价的*/
var a = try? divide(20, 0)
var b: Int?
do {
  b =  try divide(20, 0)
} catch {  b = nil }

1-5、rethrows

/*rethrows表明: 函数本身不会抛出错误, 但调用闭包参数抛出错误, 那么它会将错误向上抛*/
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
  print(try fn(num1, num2))
}
// Fatal error: Error raised at top level
try exec(divide, 20, 0)

// throws和rethrows效果一样, 都是抛出异常; 不同点是throws是其函数调用参数导致的异常, rethrows是闭包参数导致的异常

1-6、defer

/*defer语句: 用来定义以任何方式(抛错误、return等) 离开代码块前必须要执行的代码
    defer语句将延迟至当前作用域结束之前执行
*/
func divide(_ num1: Int, _ num2: Int) throws -> Int {
  if num2 == 0 {
    throw SomeError.illegalArg("0不能作为除数")
  }
  return num1 / num2
}
func open(_ filename: String) -> Int {
  print("open")
  return 0
}
func close(_ file: Int) {
  print("close")
}
func processFile(_ filename: String) throws {
  let file = open(filename)
  defer {
    close(file)
  }
  // 使用file
  // ...
  try divide(20, 0)
  
  //close 将会在这里调用
}
try processFile("test.txt")
// open
// close
// Fatal error: Error raised at top level

/*defer语句的执行顺序与定义顺序相反*/
func fn1() { print("fn1") }
func fn2() { print("fn2") }
func test() {
  defer { fn1() }
  defer { fn2() }
}
test()
// fn2
// fn1

2-1、泛型(Generics)

/*泛型可以将类型参数化, 提高代码复用率, 减少代码量*/
func swapValues<T>(_ a: inout T, _ b: inout T) {//T代表一种不确定的类型
  (a, b) = (b, a)
}
var n1 = 10
var n2 = 20
swapValues(&n1, &n2)

var d1 = 10.0
var d2 = 20.0
swapValues(&d1, &d2)

struct Date {
  var year = 0, month = 0, day = 0
}
var dd1 = Date(year: 2011, month: 9, day: 10)
var dd2 = Date(year: 2012, month: 10, day: 11)
swapValues(&dd1, &dd2)

/*泛型函数赋值给变量*/
var fn: (inout Int, inout Int) -> () = swapValues

func test<T1, T2>(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test

class Stack<E> { //泛型类
  var elements = [E]()
  func push(_ elements: E) { elements.append(element) }
  func pop() -> E { elements.removeLast() }//删除数组最后一个元素
  func top() -> E { elements.last! }//查看数组最后一个元素
  func size() -> Int { elements.count }
}
var intStack = Stack<Int>()
intStack.push(11)
intStack.push(22)
intStack.push(33)
print(intStack.top()) //33
print(intStack.pop()) //33
print(intStack.pop()) //22
print(intStack.pop()) //11
print(intStack.size()) //0
var anyStack = Stack<Any>()

class SubStack<E> : Stack<E> {}

struct Stack<E> {
  var elements = [E]()
  mutating func push(_ element: E) { elements.append(element) }
  mutating func pop() -> E { elements.removeLast() }//在结构体中修改结构体中属性的内存, 要加mutating
  func top() -> E { elements.last! }
  func size() -> Int { elements.count }
}

enum Score<T> {
  case point(T)
  case grade(String)
}
let score0 = Score<Int>.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score<Int>.grade("A")//就算没有用到point(), 也要写上T的类型

3-1、关联类型(Associated Type)

/*关联类型的作用: 给协议中用到的类型定义一个占位名称
    协议中可以拥有多个关联类型
*/
protocol Stackable {//协议想实现泛型的话, 就用关联类型.
    associatedtype Element //关联类型
  associatedtype Element2 //关联类型
  mutating func push(_ element: Element)
  mutating func pop() -> Element
  func top() -> Element
  func size() -> Int
}

class StringStack : Stackable {
    // 给关联类型设定真实类型
  // typelias Element = String //方法一
  var elements = [String]()
  func push(_ element: String) { elements.append(element) }
  func pop() -> String { elements.removeLast() }
  func top() -> String { elements.last! }
  func size() -> Int { elements.count }
}
var ss = StringStack()
ss.push("Jack")
ss.push("Rose")

class Stack<E> : Stackable {
  // typealias Element = E
  var elements = [E]()
  func push(_ element: E) {
    elements.append(element)
  }
  func pop() -> E { elements.removeLast() }
  func top() -> E { elements.last! }
  func size() -> Int { elements.count }
}

3-2、类型约束

protocol Runnable { }
class Person { }
func swapValues<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
  (a, b) = (b, a)
}

protocol Stackable {
  associatedtype Element: Equatable//这里指定了关联类型遵守Equatable协议(系统自带的:Equatable可等价)
}
class Stack<E : Equatable> : Stackable { typelias Element = E }//因为协议使用了关联类型, 此处必须指明关联类型是什么

func equal<S1: Stackable, S2: Stackable>(_ s1: S1,_ s2: S2)->Bool where S1.Element == S2.Element, S1.Element : Hashable {//指定了泛型要遵守Stackable协议,where条件约束: S1的关联类型Element跟S2的关联类型Element是相等的, 指定S1关联类型Element遵守Hashable协议(系统自带的:Hashable可哈希)
  return false
}

var s1 = Stack<Int>()
var s2 = Stack<Int>()
var s3 = Stack<String>()
equal(s1, s2)
equal(s1, s3) // error: requires the types 'Int' and 'String' be equivalent

3-3、协议类型的注意点

protocol Runnable {}
class Person : Runnable {}
class Car: Runnable {}

func get(_ type: Int) -> Runnable {
  if type == 0 {
    return Person()
  }
  return Car()
}
var r1 = get(0)
var r2 = get(1)

/*如果协议中有associatedtype*/
protocol Runnable {
  assciatedtype Seed
  var speed: Speed { get }
}
class Person : Runnable {
    var speed: Double { 0.0 }
}
class Car : Runnable {
  var speed: Int { 0 }
}

func get(_ type: Int) -> Runnable {//报错: 因为无法编译时期确定speed类型
  if type == 0 {
    return Person()
  }
  return Car()
}
var r1 = get(0)
var r2 = get(1)

/*泛型解决*/
// 解决方法①: 使用泛型
func get<T: Runnable>(_ type: Int) -> T {//返回值类型是泛型, 且遵守Runnable协议
  if type == 0 {
    return Person() as! T
  }
  return Car() as! T
}
var r1: Person = get(0)
var r2: Car = get(1)

// 解决方案②: 使用some关键字声明一个不透明类型(Opaque Type),目的:不公开内部细节,只开发部分接口
func get(_ type: Int) -> some Runnable {//带有Runnable的协议作为返回类型的话.some设定: 要求只能返回一种, 不能返回两种类型.
//  if type == 0 {
//    return Person()
//  }
  return Car()
}
var r1 = get(0)
var r2 = get(1)

/// some除了用在返回值类型上, 一般还可以用在属性类型上
protocol Runnable { associatedtype Speed }
class Dog : Runnable { typealias Speed = Double }
class Person {
  var pet: some Runnable {// 养了一只宠物,又不想让人知道养的什么宠物.只想让人知道它是遵守Runnable协议的
    return Dog()
  }
}

第十三章 汇编分析String、Array底层

1-1、关于String的思考

/*1个String变量占用多少内存?
    下面2个String变量, 底层存储有什么不同?
*/
var str1 = "0123456789"//全局变量 16个字节
var str2 = "0123456789ABCDEF"

/*内存地址从低到高:
代码区->常量区->全局区 (数据段)->堆空间->栈空间->动态库
*/

/*如果对String进行拼接操作, String变量的存储会发生什么变化?*/
str1.append("ABCDE")
str1.append("F")

str2.append("G")

/*ASCII码表: https://www.ascii-code.com/ */

var str1 = "0123456789"//如果字符串长度小于等于F,会直接将字符串数据存在字符串内存里面
// 0x3736353433323130 0xea00000000003938
// 0xea   里面的a表示长度,此时是10位,所以是a; Oxe表示类型标识
// 类似于OC的tagger pointer
print(Mems.memStr(ofVal: &str1))

// var str1 = "0123456789ABCDE"
// 0x3736353433323130 0xef45444342413938
// print(Mems.memStr(ofVal: &str1))

var str2 = "0123456789ABCDEF"//调用str2的初始化器
// 0xd0000000000010 0x800000010000a790 //0x800000010000a790(后一段)这8个字节中决定了字符串的真实地址值,实际内容放在地址值指向的内存里; 0xd0000000000010(前一段)中的10代表字符串的长度,0xd0是标志位.
// 字符串的真实地址 + 0x7fffffffffffffe0 = 0x800000010000a790
// 字符串的真实地址 = 0x800000010000a790 - 0x7fffffffffffffe0 
// 字符串的真实地址 = 0x000000010000a790 + 0x20 (小技巧,此技巧只针对常量区) 

// movabsq $0x7fffffffffffffe0, %rdx
// addq %rdx, %rdi

// 0x10000A780是"0123456789ABCDEF"的真实地址

// %rdi存放着字符串的真实地址
// %rsi存放的是字符串的长度0x10
// callq String.init
// %rdx存放的是 字符串的真实地址 + 0x7fffffffffffffe0
print(Mems.memStr(ofVal: &str2))//此时字符串内容已经不存在这16个字节里了
/*这串字符串内容放在全局区*/

1-2、从编程到启动APP

/*从编程到启动APP的过程:
OC、Swift源码 --(编译、连接)-> Mach-O二进制可执行文件(可执行程序) --(启动)-> Mach-O(代码区、全局区、常量区在这里)和动态库(Mach-O用到的动态库,程序运行过程中动态加载的) 载入内存.
*/

/*不同系统转换的可执行文件不同:
     MaciOS: Mach-O
     Windows: PE
     Linux: ELF
*/

/*窥探Mach-O文件:
    sp1.项目中右击Products下面的Mach-O文件,选择show in finder, 然后将其拷贝到桌面
    sp2.通过工具"MachOView fG!`s branch", File >> Open...

在Mach-O文件中查看文件内存:
比如pFile(文件偏移--也就是从文件最开始位置往下数是第几个字节)如果是0000D7B8,它在内存中其实就是0x100000000 + 0xD7B8 //0x100000000是VM address(虚拟内存地址)


(_TEXT._cstring) 常量区
(_DATA._data) 全局区
*/

/*malloc断点查找
*/
class Person { }
var p = Person()//类创建好的实例肯定放堆空间


/*总结: 
// 字符串长度 <= 0xF, 字符串内容直接存放在str1变量的内存中
var str1 = "0123456789"

// 字符串长度 > 0xF, 字符串内容存放在__TEXT.cstring中(常量区)
// 字符串的地址值信息存放在str2变量的后8个字节中
var str2 = "0123456789ABCDEFGHIJ"

// 由于字符串长度 <= 0xF, 所以字符串内容依然存放在str1变量的内存中
str1.append("ABCDE")
// 开辟堆空间
str1.append("F")

// 开辟堆空间
str2.append("G")
*/

1-3、dyld_stub_binder

/*符号绑定器
    将函数的真实地址,放到我们的数据段里面去
*/

/*符号的延迟绑定通过dyld_stub_binder完成*/

/*jmpq *0xb31(%rip)格式的汇编指令
    占用6个字节
*/

2-1、关于Array的思考

public struct Array<Element>
// 一个Int占8个字节
var arr = [1,2,3,4] 
print(MemoryLayout.stride(ofValue: arr)) // 8
print(MemoryLayout<Array<Int>>.stride) // 8

print(Mems.ptr(ofVal: &arr))//arr变量的内存地址值
print(Mems.memStr(ofRef: arr))//arr变量的内存空间的东西

/*1个Array变量占用多少内存?
        8个字节, 存放的是堆空间的内存地址值.

    数组中的数据存放在哪里?
        堆空间
*/

/*arr数组内存空间里的字节---> 前8个字节: 未知 --> 后面8个字节放: 引用技术 --> 再后面8个字节放: 元素数量  --> 再后面8个字节放: 数组容量? --> 再后面是真实数据.
*/

/*array和string行为上是值类型, 底层实际上是引用类型.*/

第十四章 可选项的本质、运算符重载、扩展

1-1、可选项的本质

/*可选项的本质是enum类型*/
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
  case some(Wrapped)//关联值
  public init(_ some: Wrapped)
}

var age: Int? = 10
//本质代码是👇
var age0: Optional<Int> = Optional<Int>.some(10)
var age1: Optional = .some(10)
var age2 = Optional.some(10)
var age3 = Optional(10)
age = nil
age3 = .none

var age: Int? = nil
//本质上等同于这么写:
var age0 = Optional<Int>.none
var age1: Optional<Int> = .none

var age: Int? = .none
age = 10
age = .some(20)
age = nil

/*可选类型支持switch, 用switch判断可选类型*/
switch age {
case let v?:
  print("some", v)
case nil:
  print("none")
}

// 本质上是:
switch age {
case let .some(v):
  print("some", v)
case .none:
  print("none")
}

//等价于:
if let v = age {
  print("some", v)
} else {
  print("none")
}

1-2、多重可选项

var age_: Int? = 10
var age: Int?? = age_
age = nil

//本质上是👇
var age0 = Optional.some(Optional.some(10))
age0 = .none
var age1: Optional<Optional> = .some(.some(10))
age1 = .none

var age: Int?? = 10
var age0: Optional<Optional> = 10

2-1、溢出运算符(Overflow Operator)

/*Swift的算数运算符出现溢出时会抛出运行时错误*/
print(Int8.min)// -128
print(Int8.max)// 127
print(UInt8.min)// 0
print(UInt8.max)// 255
//UInt的取值范围是0~255

var v: UInt8 = UInt8.max
v += 1 //报错

/*Swift有溢出运算符(&+、&-、&*), 用来支持溢出运算. (超出最大/最小值后会循环回去)*/
var v1: UInt8 = UInt8.max
v2 = v1 &+ 1 // 0

var v1: UInt8 = UInt8.min
v2 = v1 &- 1 // 255

var v1: UInt8 = UInt8.max
v2 = v1 &* 2 // 254 解析:相当于 v1 &+ v1,即255 &+ 255 > 254

2-2、运算符重载(Operator Overload)

/*类、结构体、枚举可以为现有的运算符提供自定义的实现, 这种操作叫做: 运算符重载*/
struct Point {
  var x: Int, y: Int
}

// 重载: 函数名相同, 功能不一样
func + (p1: Point, p2: Point) -> Point {
  Point(x: p1.x +p2.x, y: P1.y + p2.y)
}

let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
porint(p) // Point(x: 21, y: 42)
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 10, y: 20)
var p3 = Point(x: 10, y: 20)
let p4 = p1 + p2 + p3 // 重载的结合性
print(p4)

//优化上面的代码: 重载建议写到类型里面去.如下👇
struct Point {
  var x: Int, y: Int
  static func + (p1: Point, p2: Point) -> Point {
    Point(x: p1.x + p2.x, y: P1.y + p2.y)
  }
  static func - (p1: Point, p2: Point) -> Point {//中缀运算符
    Point(x: p1.x - p2.x, y: P1.y - p2.y)
  }
  static prefix func - (p1: Point) -> Point {//前缀运算符
    Point(x: -p1.x, y: -P1.y)
  }
  static func += (p1: inout Point, p2: Point) {
    p1 = p1 + p2
  }
  static prefix func ++ (p: inout Point) -> Point {
    p += Point(x: 1, y: 1)
    return p
  }
  static postfix func ++ (p: inout Point) -> Point {//后缀运算符
    let tmp = p
    p += Point(x: 1, y: 1)
    return tmp
  }
  static func == (p1: Point, p2: Point) -> Bool {
    (p1.x == p2.x) && (p1.y == p2.y)
  }
}

var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 10, y: 20)
var p3 = -p2//前缀运算符
print(p3)
p1 += p2
print(p1)

2-2、Equatable

/*要想得知2个实例是否等价, 一般做法是遵守Equatable协议,重载 == 运算符
    与此同时,等价于重载了 != 运算符
*/
class Person : Equatable { //因为是重载运算符, 可省略: Equatable 
  var age = 0
  init(age: Int) {
    self.age = age
  }
  //定义一个规则, 只要发现这两个人的年龄相等,就认为这两个人等价
  static func == (lhs: Person, rhs: Person) -> Bool {
    lhs.age == rhs.age
  }
}
var p1 = Person(age: 10)
var p2 = Person(age: 10)
print(p1 == p2)
print(p1 != p2)


/*只要遵守了Equatable协议, 说明可以用等价运算符来进行比较的,也就具备了等价性判断*/
func eqauls<T : Equatable>(_ t1: T, _ t2: T) -> Bool {
  t1 == t2
} 
equals(Person(),Person())

/*Swift为以下类型提供默认的 Equatable 实现:*/
// 1.没有关联类型的枚举
enum Answer {
    case wrong
  case right
}
var s1 = Answer.wrong
var s2 = Answer.right
print(s1 == s2)
// 2.只拥有遵守 Equatable 协议关联类型的枚举
class Cat {}
enum Answer : Equatable {
    case wrong(Int, String)//Int和String内部默认遵守Equatable协议
  case right(Cat)
}
var s1 = Answer.wrong(10, "Jack")
var s2 = Answer.wrong(20, "Rose")
print(s1 == s2)
var s3 = Answer.right(Cat())
var s4 = Answer.right(Cat())
print(s3 == s4)//报错: 因为Cat没有遵守Equatable协议
// 3.只拥有遵守 Equatable 协议存储属性的结构体.(类不包括)
struct Point : Equatable {
  var x = 0 , y = 0
  static func == (p1: Point, p2: Point) -> Bool {
    (p1.x == p2.x) && (p1.y == p2.y)
  }
}

/*引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 ===、!==*/

2-3、Comparable

/*要想比较2个实例的大小, 一般做法是: 
    遵守 Comparable 协议
    重载相应的运算符
*/
// score大的比较大, 若score相等, age小的比较大
struct Student : Comparable {
  var age: Int
  var score: Int
  init(score: Int, age: Int) {
    self.score = score
    self.age = age
  }
  static func < (lhs: Student, rhs: Student) -> Bool {
    (lhs.score < rhs.score) || (lhs.score == rhs.score && lhs.age > rhs.age)
  }
  static func > (lhs: Student, rhs: Student) -> Bool {
    (lhs.score > rhs.score) || (lhs.score == rhs.score && lhs.age < rhs.age)
  }
  static func <= (lhs: Student, rhs: Student) -> Bool {
    !(lhs > rhs)
  }
  static func >= (lhs: Student, rhs: Student) -> Bool {
    !(lhs < rhs)
  }
}

var stu1 = Student(score: 100, age: 20)
var stu2 = Student(score: 98, age: 18)
var stu3 = Student(score: 100, age: 20)
print(stu1 > stu2) // true
print(stu1 >= stu2) // true
print(stu1 >= stu3) // true
print(stu1 <= stu3) // true
print(stu2 < stu1) // true
print(stu2 <= stu1) // true

2-4、自定义运算符(Custom Operator)

/*可以自定义新的运算符: 在全局作用域使用operator进行声明
  prefix operator 前缀运算符
  postfix operator 后缀运算符
  infix operator 中缀运算符 : 优先级组
*/

/*
precedencegroup 优先级组 {
    associativity: 结合性(left\right\none)
    higherThan: 比谁的优先级高
    lowerThan: 比谁的优先级低
    assignment: true 代表在可选链操作中拥有跟赋值运算符一样的优先级
}
*/

/*
prefix operator +++
infix operator +- : PlusMinusPrecedence
percedencegroup PlusMinusPrecedence {
    associativity: none //结合性 a1 + a2 + a3, 为none则表示不能结合使用
    higherThan: AdditionPrecedence //比加号的优先级高
    lowerThan: MultiplicationPrecedence //比减号的优先级低
    assignment: true //
}
*/
/*Apple文档参考:              https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations

https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380   
*/

//例子如下:
prefix operator +++
prefix func +++ (_ i: inout Int) {
  i += 2
}
var age = 10
+++age

//解释assignment: true
struct Point {
  var x = 0, y = 0
  static func +- (p1: Point, p2: Point) -> Point {
    Point(x: p1.x + p2.x, y: p1.y - p2.y)
  }
}
class Person? = Person()
p?.point +- Point(x: 10, y:20) //如果p有值, 才会执行后面的运算

3-1、扩展(Extension)

/*
Swift中的扩展, 有点儿类似于OC中的分类( Category )
扩展可以为枚举、结构体、类、协议添加新功能
    可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
扩展不能办到的事情
    不能覆盖原有的功能
    不能添加存储属性, 不能向已有的属性添加属性观察器
    不能添加父类
    不能添加指定初始化器, 不能添加反初始化器
    ...
*/
extension Double {// 扩展增加计算属性
  var km: Double { self * 1_000.0 }
  var m: Double { self }
  var dm: Double { self / 10.0 }
  var cm: Double { self / 100.0 }
  var mm: Double { self / 1_000.0 }
}

extension Array {// 扩展增加方法: 下标判断,发现数组越界了,就返回nil
  subscript(nullable idx: Int) -> Element? {//原有类的泛型,在扩展中一样可用.比如: Element代表数组中的元素
    if(startIndex..<endIndex).contains(idx) {
      return self[idx]
    }
    return nil
  }
}
var arr: Array<Int> = [10, 20, 30]
print(arr[nullable: 10] ?? 0)

extension Int {
  func repetitions(task: () -> Void) {
    for _ in 0..<self { task() }
  }
  mutating func square() -> Int {// 扩展结构体mutating的方法
    self = self * self
    return self
  }
  enum Kind { case negative, zero, positive }
  var kind: Kind {// 扩展嵌套类型: 判断复数、0、正数
    switch self {
      case 0: return .zero
      case let x where x > 0 : return .positive
      default: return .negative
    }
  }
  subscript(digitIndex: Int) -> Int {
    var decimalBase = 1
    for _ in 0..<digitIndex { decimalBase *= 10 }
    return (self / decimalBase) % 10
  }
}

3.repeats {//重复打印3次
  print(1)
}
var age = 456
print(age[0])//获取下标对应数字

3-2、扩展 协议、初始化器

class Person {
  var age: Int
  var name: String
  init(age: Int, name: String) {
    self.age = age
    self.name = name
  }
}
extension Person : Equatable { //给Person扩展协议
  static func == (left: Person, right: Person) -> Bool {
    left.age == right.age && left.name == right.name
  }
  convenience init() {//给类扩展便捷初始化器,指定初始化器不能给类扩展
    self.init(age: 0, name: "")
  }
}


struct Point {
  var x: Int = 0
  var y: Int = 0
}
extension Point {
  init(_ point: Point) {//给结构体扩展初始化器(注意:便捷初始化器和指定初始化器是类才有的概念.)
    self.init(x: point.x, y:point.y)
  }
}
var p1 = Point()//编译器自带初始化器
var p2 = Point(x: 10)//编译器自带初始化器
var p3 = Point(y: 20)//编译器自带初始化器
var p4 = Point(x: 10, y: 20)//编译器自带初始化器
var p5 = Point(p4)//扩展初始化器
/*
    如果希望自定义初始化器的同时, 编译器也能够生成默认初始化器, 那么可以在扩展汇总编写自定义初始化器
    required初始化器也不能写在扩展中
*/

protocol Runnable {
  init(age: Int)
}
class Person {}
extension Person : Runnable {
  required init(age: Int){//报错: required初始化器必须直接声明在'Person'类里面
    
  }
}

3-3、协议

/*如果一个类型已经实现了协议的所有要求, 但是还没有声明它遵守了这个协议, 那么可以通过扩展来让它遵守这个协议*/
protocol TestProtocol {
  func test()
}
class TestClass {
  func test() {
    print("test")
  }
}
extension TestClass : TestProtocol {}

/// 例子: 编写一个函数, 判断一个整数是否为奇数
//第一种做法:
func isOdd<T: BinaryInteger>(_ i: T) -> Bool { i % 2 != 0 }//只要是整数的共同点是: 都遵守BinaryInteger协议
print(isOdd(4))

//第二种做法: 给BinaryInteger协议扩展方法
extension BinaryInteger {
    func isOdd() -> Bool { self % 2 != 0 }
}
print(10.isOdd())
print((-3).isOdd())

/*扩展可以给协议提供默认实现, 也间接实现[可选协议]的效果
    扩展可以给协议扩充[协议中从未声明过的方法]
*/
protocol TestProtocol {
  func test1()
}
extension : TestProtocol {
  func test1() {
    print("TestProtocol test1")
  }
  func test2() {
    print("TestProtocol test2")
  }
}

//场景一
class TestClass : TestProtocol {} //因为上面扩展中实现了TestProtocol的方法, 所以不会报错.
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2
var cls2 : TestProtocol = TestClass()
cls2.test1() // TestProtocol test1
cls2.test2() // TestProtocol test2

//场景二
class TestClass : TestProtocol {
  func test1() { print("TestClass test1") } 
  func test2() { print("TestClass Test2") }
}
var cls = TestClass()//优先调用自己的实现
cls.test1() // TestClass test1
cls.test2() // TestClass test2
var cls2 : TestProtocol = TestClass()//编译器一旦发现它是TestProtocol类型,它就知道一旦你调用test2, test2可能在实例里面是不存在的,它就直接当做它不存在.所以他就不去实例里面找了, 优先去协议里面找
cls2.test1() // TestClass test1 解析: 协议里面声明了这个方法, 那么这个方法必然要实现, 所以这里虽然声明成实例类型, 调用test1, 肯定优先实例里面找.我协议里面有,你实例里面肯定有的
cls2.test2() // TestProtocol test2 解析:如果你要调用一个方法, 是协议声明里没有的, 而这里又定义成协议类型, 它会认为你的实例里面没有test2, 它就直接去协议扩展里面优先找到test2了,所以是这样一个结果

3-4、协议里的泛型

class Stack<E> {
  var elements = [E]()
  func push(_ element: E){
    elements.append(element)
  }
  func pop() -> E { elements.removeLast() }
  func size() -> Int { elements.count }
}
// 扩展中依然可以使用原类型中的泛型类型
extension Stack {
    func top() -> E { elements.last! }
}
// 符合条件才扩展
extension Stack : Equatable where E: Equatable {
    static func == (left: Stack, right: Stack) -> Bool {
    left.elements == right.elements
  }
}

第十五章 访问控制、内存管理

1-1、遵守CustomStringConvertible、CustomDebugStringConvertible协议,都可以自定义实例的打印字符串

class Person : CustomStringConvertible, CustomDebugStringConvertible {
  var age = 0
  var description: String { "person_\(age)" }
  var debugDescription: String { "debug_person_\(age)"}
}
var person = Person()
print(person) // person_0
debugPrint(person) //debug_person_0

/*
print调用的是CustomStringConvertible协议的description
debugPrint、po调用的是CustomDebugStringConvertible协议的debugDescription
*/

1-2、Self

/*Self一般用作返回值类型, 限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)*/
protocol Runnable {
  func test() -> Self
}
class Person : Runnable {
  reuqired init() {}
  func test() -> Self { type(of: self).init() }
}
class Student : Person {}

var p = Person()
// Person
print(p.test())

var stu = Student()
// Student
print(stu.test())

/*另一个用途:  Self代表当前类型*/
class Person {
  var age = 1
  static var count = 2
  func run(){
    print(self.age) //1
    print(Self.count) //2
  }
}

1-3、错误处理--断言( assert )

/*
    很多编程语言都有断言机制: 不符合指定条件就抛出运行时错误, 常用于调试(Debug)阶段的条件判断
    默认情况下, swift的断言只会在Debug模式下生效, Release模式下会忽略
*/
func divide(_ v1: Int, _ v2: Int) -> Int {
  assert(v2 != 0, "除数不能为0")//条件成立的话就会继续往下进行, 条件不成立, 就会抛出异常,导致崩溃,并且抛出后面的信息.
  return v1 / v2
}
print(divide(20, 0))
/*与自定义错误的区别:
    如果希望抛出的错误能被do-catch捕捉到, 而且捕捉到后还能做出相应的反应, 不至于崩溃. 就用自定义错误, 自己抛出;
    如果希望在调试阶段, 就发现条件不对, 就崩溃, 不能被do-catch拦截,就用assert.
*/

/*
    增加Swift Flags修改断言的默认行为(Build Settings >> Other Swift Flags)
        -assert-config Release : 强制关闭断言
        -assert-config Debug : 强制开启断言
*/

1-4、fatalError

/*
如果遇到严重问题, 希望结束程序运行时, 可以直接使用fatalError函数抛出错误(这是无法通过do-catch捕捉的错误)
    使用fatalError函数, 就不需要再写return
*/
func test(_ num: Int) -> Int {
    if num >= 0 {
    return 1
  }
  fatalError("num不能小于0")//闪退
}
/*与assert相似, 对比assert断言:
    不区分Release和Debug, 都好使, 都闪退.
*/

/*在某些不得不实现、但不希望别人调用的方法, 可以考虑内部使用fatalError函数*/
class Person { required init() {} }
class Student : Person {
  required init() { fatalError("don`t call Student.init")}
  init(score: Int) {}
}
var stu1 = Student(score: 98)
var stu2 = Student()

2-1、访问控制 (Access Control)

/*
    在访问权限控制这块, Swift提供了5个不同的访问级别 (以下是从高到低排列, 实体指被访问级别修饰内容)
        > open: 允许在定义实体的模块、其他模块中访问, 允许其他模块进行继承、重写( open只能用在类, 类成员上)
        > public: 允许在定义实体的模块、其他模块中访问, 不允许其他模块进行继承、重写
        > internal : 只允许在定义实体的模块中访问, 不允许在其他模块中访问.
        > fileprivate: 只允许在定义实体的源文件中访问
        > private: 只允许在定义实体的封闭声明中访问

绝大部分实体默认都是internal级别
*/

/*访问级别的使用准则
一个实体不可以被更低访问级别的实体定义, 比如:
    变量/常量类型 >= 变量/常量
    参数类型、返回值类型 >= 函数
    父类 >= 子类
    父协议 >= 子协议
    原类型 >= typealias
    原始值类型、关联值类型 >= 枚举类型
    定义类型A时用到的其他类型 >= A
    ...
*/

//例子: 变量/常量类型 >= 变量/常量
fileprivate class Person { }
internal var person: Person //报错: 类型级别<变量级别 导致的

2-2、元组类型的访问级别

/*元组类型的访问级别是所有成员类型最低的那个*/
internal struct Dog {}
fileprivate class Person {}

// (Dog, Person)的访问级别是fileprivate
fileprivate var data1: (Dog, Person)
private var data2: (Dog, Person)

2-3、泛型类型的访问级别

/*泛型类型的访问级别是 类型的访问级别 以及 所有泛型类型参数的访问级别 中最低的那个*/
internal class Car {}
fileprivate class Dog {}
public class Person<T1, T2>{ }

// Person<Car, Dog>访问级别是fileprivate
fileprivate var p = Person<Car, Dog>()

2-4、成员、嵌套类型

/*
类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别.
    一般情况下, 类型为private或fileprivate, 那么成员/嵌套类型默认也是private或fileprivate
    一般情况下, 类型为internal或public, 那么成员/嵌套类型默认是internal
*/
public class PublicClass {
    public var p1 = 0
  fileprivate func f1() {} //fileprivate
  private func f2() {} //private
}
class InternalClass {//internal
  var p = 0 //internal
  fileprivate func f1() {}//fileprivate
  private func f2() {} //private
}

fileprivate class FilePrivateClass { // fileprivate
  func f1() {} //fileprivate
    private func f2() {} //private
}
private class PrivateClass { // private
  func f() {} //private
}
/*子类重写的成员访问级别必须 >= 父类的成员访问级别*/

/// 下面代码能否编译通过?
private class Person {}
fileprivate class Student : Person {} //场景不同,决定是否报错

private struct Dog { //此时private相当于fileprivate,里面的成员也相当于fileprivate
  var age: Int = 0
  func run() {}
}
fileprivate struct Person {
  var dog: Dog = Dog()
  mutating func walk() {
    dog.run()
    dog.age = 1
  }
}

private Struct Dog {
    private var age: Int = 0
  private func run() {}
}
fileprivate struct Person {//报错
  var dog: Dog = Dog()
  mutating func walk() {
    dog.run()
    dog.age = 1
  }
}
/*直接在全局作用域下定义的private等价于fileprivate*/

2-4-5、成员的重写

/*
    ①子类重写成员的访问级别必须 >= 子类的访问级别, 或者 >= 父类被重写成员的访问级别

    ②父类的成员不能被成员作用域外定义的子类重写
*/

// ②
public class Person {
  private var age: Int = 0
}
public class Student : Person {
  override var age : Int{//报错
    set {}
    get {10}
  }
}
// 改为下面这样写👇
public class Person {
  private var age: Int = 0
  public class Student : Person {
    override var age: Int {
      set {}
      get {10}
    }
  }
}

// ① 成员访问级别跟协议很像 , 访问级别 >= 子类访问级别或在父类所重写成员 两个中最小的一个就行了
class Person {
  internal func run() { }
}
fileprivate class Student : Person {
  fileprivate override func run() { }
}

2-5、getter、setter

/*getter、setter默认自动接收他们所属环境的访问级别
    可以给setter单独设置一个比getter更低的访问级别, 用以限制写的权限
*/
fileprivate(set) public var num = 10
class Person {
  private(set) var age = 0
  fileprivate(set) public var weight: Int {
    set {}
    get { 10 }
  }
  internal(set) public subscript(index: Int) -> Int {
    set {}
    get { index }
  }
}

2-6、初始化器

/*
    ①如果一个public类想在另一个模块调用编译生成的默认无参初始化器, 必须显示提供public的无参初始化器
        因为public类的默认初始化器是internal级别
    ②required初始化器 >= 它的默认访问级别
    ③如果结构体有private/fileprivate的存储实例属性,那么它的成员初始化器也是private/fileprivate
        否则默认就是internal
*/

// ①
// 在动态库xxx.dylib中
public class Person {
  //如果这个类型默认是public,那么内部的所有成员默认都是internal
  public init() { //这里要写public,才能被其他模块调用
  }
} 
// 在TestSwift中
var p = Person() //报错

2-7、枚举类型的case

/*不能给enum的每个case单独设置访问级别
    每个case自动接收enum的访问级别
    public enum定义的case也是public
*/
public enum Season {
    case spring, summer, autumn winter//public
}

2-8、协议

/*
①协议中定义的要求自动接收协议的访问级别, 不能单独设置访问级别
    public协议定义的要求也是public

②协议实现的访问级别必须 >= 类型的访问级别, 或者 >= 协议的访问级别
*/

//①
public protocol Runnable {
    func run() //public
}

//②
fileprivate protocol Runnable {
  func run()
}
public class Person : Runnable {
  fileprivate func run() {
    
  }
}

/*下面代码能不能编译通过?*/
public protocol Runnable {
  func run() // public
}
public class Person : Runnable {
  func run() {}//internal 编译报错
}

2-9、扩展

/*如果有显示设置扩展的访问级别, 扩展添加的成员自动接收扩展的访问级别*/
class Person {}
fileprivate extension Person {
  func run() {}
}
/*如果没有显示设置扩展访问级别,扩展添加的成员默认的访问级别, 跟直接在类型中定义的成员一样*/
/*可以单独给扩展添加的成员设置访问级别*/
/*不能给用于遵守协议的扩展显式设置扩展的访问级别*/

/*
在同一文件的扩展, 可以写成类似多个部分的类型声明
    在原本的声明中声明一个私有成员, 可以在同一文件的扩展中访问它
    在扩展中声明一个私有成员, 可以在同一文件的其他扩展中、原本声明中访问它
*/
// 在同一个文件中
public class Person {
  private func run0()
  private func eat0() {
    run1()
  }
}
extension Person {
  private func run1() {}
  private func eat1() {
    run0()
  }
}
extension Person {
  private func eat2() {//相当于直接写在了类型里
    run1()
  }
}

2-10、将方法赋值给var/let

/*方法也可以像函数那样, 赋值给一个let或者var*/
struct Preson {
  var age: Int
  static func run(_ v: Int) { print("static func run", v) }
}
// var fn1: (Person) -> (int) -> () = Person.run
var fn1 = Person.run //实例方法赋值给fn1
var fn2 = fn1(Person(age: 10)) //带有person实例的run方法
fn2(20) //拿出实例调用run, 然后传个20
//等同于下面👇
Person(age:10).run(20)

struct Preson {
  var age: Int
  static func run(_ v: Int) { print("static func run", v) }
}
var fn  = Person.run
fn(20)

3-1、内存管理

/*
跟OC一样, Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)

Swift的ARC中有3种引用
    1.强引用(strong reference) : 默认情况下, 引用都是强引用
    2.弱引用(weak reference) : 通过weak定义弱引用
        必须是可选类型的var, 因为实例销毁后, ARC会自动将弱引用设置为nil.
        ARC自动给弱引用设置为nil时, 不会触发属性观察器
    3.无主引用(unowned reference) : 通过unowned定义无主引用
        不会产生强引用, 非可选类型, 实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)
        试图在实例销毁后访问无主引用, 会产生运行时错误(野指针)
*/

class Person {
  weak var dog: Dog? {
    willSet {}
    didSet {}
  }
    deinit {
    print("Person.deinit")
  }
}

print(1)
weak var p: Person? = Person()
print(2)
print(3)

var p = Person()
p.dog = nil //不会触发属性观察器

3-2、weak、unowned的使用限制

/*weak、unowned只能用在类实例上面*/
protocol Livable : AnyObject {}//写上:AnyObject,代表将来这个协议只能被类遵守
class Person  {}

weak var p0: Person?
weak var p1: AnyObject?
weak var p2: Livable?

unowned var p10: Person?
unowned var p11: AnyObject?
unowned var p12: Livable?

3-3、Autoreleasepool

//自动释放池
public func autoreleasepool<Result>(invoking body: () throws -> Result) rethrows -> Result

//使用
autoreleasepool {
    let p = MJPerson(age: 20, name: "Jack")
  p.run()
}

3-4、循环引用(Reference Cycle)

/*
weak、unowned 都能解决循环引用的问题, unowned 要比 weak 少一些性能消耗
    在生命周期中可能会变为 nil 的使用 weak
    初始化赋值后再也不会变为 nil 的使用 unowned
*/

3-5、闭包的循环引用

/*
    闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
    下面代码会产生循环引用, 导致Person对象无法释放(看不到Person的deinit被调用)
*/
class Person {
  var fn: (() -> ())?
  func run() { Print("run") }
  deinit{ print("deinit") }
}
func test() {
  let p = Person()
  p.fn = { p.run() }//产生了强引用
}
test()

/*在闭包表达式的捕获列表声明weak或unowned引用,解决循环引用问题*/
p.fn = { 
  [weak p] in //[weak p]是捕获列表
  p?.run()//弱引用必须是可选类型, 所以p前加?
}

p.fn = {
  [unowned p] in
  p.run()//unowned默认是非可选类型, 所以要去掉p前的?
}

p.fn = {
  [weak wp = p, unowned up = p, a = 10 + 20] in //在捕获列表里定义新的名称
  wp?.run()
}

/*如果想在定义闭包属性的同时引用self, 这个闭包必须是lazy的(因为在实例初始化完毕之后才能引用self)*/
class Person {
  lazy var fn: (() -> ()) = {//lazy 意味着调用之前这里是空的
    [weak self] in
    self?.run()//要等初始化之后才能执行
  }//声明一个闭包表达式
  func run() { print("rum") }
  deinit { print("deinit") }
}
var p = Person()
p.fn()
/*上面的闭包fn内部如果用到了实例成员(属性、方法)
    编译器会强制要求明确写出self
*/

/*如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用后, 闭包的生命周期就结束了)*/
class Person {
  var age: Int = 0
  lazy var getAge: Int = {
    self.age
  }()//声明一个闭包表达式,马上调用
  deinit { print("deinit") }
}
func test() {
  var p = Person()
    print(p.getAge)
}
test()

3-6、@escaping

/*非逃逸闭包、逃逸闭包, 一般都是当做参数传递给函数*/
/*非逃逸闭包: 闭包调用发生在函数结束前, 闭包调用在函数作用域内*/
/*逃逸闭包: 闭包有可能在函数结束后调用, 闭包调用逃离了函数的作用域, 需要通过@escaping声明*/

import Dispatch //导入GCD的模块
typealias Fn = () -> ()

// fn是非逃逸闭包
func test1(_ fn: Fn) { fn() }//在函数内部调用闭包,所以是非逃逸闭包

// fn是逃逸闭包: 有可能在函数外面调用闭包, 所以是逃逸闭包
var gFn: Fn?
func test2(_ fn: @escaping Fn) { gFn = fn }

// fn是逃逸闭包 : 有可能在函数外面调用闭包,存在一定风险, 所以是逃逸闭包
func test3(_ fn: @escaping Fn) {
  DispatchQueue.global().sync {//全局队列, 异步调用
    fn()
  }
}

class Person {
    var fn: Fn 
  // fn是逃逸闭包
  init(fn: @escaping Fn) {
    self.fn = fn
  }
  func run() {
    // DispatchQueue.global().sync也是一种逃逸闭包
    // 它用到了实例成员(属性、方法), 编译器会强制要求明确写出self
    DispatchQueue.global().sync {
        self.fn()
    }
  }
}

3-7、逃逸闭包的注意点

/*逃逸闭包不可以捕获inout参数*/
typealias Fn = () -> ()
func other1(_ fn: Fn) { fn() }
func other2(_ fn: @escaping Fn) { fn() }
func test(value: inout Int) -> Fn {
  other1 { value += 1 }
 
  other2 { value += 1 }// 报错error: 逃逸闭包不能捕获inout参数
  
  func plus() { value += 1 }
  return plus// 报错error: 这里相当于也返回了一个闭包,逃逸闭包不能捕获inout参数
}

第十六章 内存访问冲突、指针

1-1、do

/*除了do-catch外, 还可以使用 do 实现局部作用域*/
do {
  let dog1 = Dog()
  dog1.age  =10
  dog1.run()
}

do {
  let dog2 = Dog()
  dog2.age  = 10
  dog2.run()
}

2-1、内存访问冲突 ( Conflicting Access to Memory)

/*
内存访问冲突会导致编译报错或运行时报错.

内存访问冲突会在两个访问满足下列条件时发生 :
    至少一个是写入操作
    他们访问的是同一块内存
    它们的访问时间重叠(比如在同一个函数内)
*/

// 不存在内存访问冲突
func plus(_ num: inout Int) -> Int { num + 1 }
var number = 1
number = plus(&number)

// 存在内存访问冲突
// Simulations accesses to 0x0, but modification requires exclusive access
var step = 1
func increment(_ num: inout Int) { num += step }
increment(&step)

// 解决内存访问冲突
var step = 1
func increment(_ num: inout Int) { num += step }
// increment(&step)
var copyOfStep = step
increment(&copyOfStep)
step = copyOfStep

2-2、下面是几个内存访问冲突的情况:

// 在全局作用域中写以下代码:
/*①*/
func balance(_ x: inout Int, _ y: inout Int) {
  let sum = x + y
  x = sum / 2
  y = sum - x
}
var num1 = 42
var num2 = 30
balance(&num1, &num2) // OK
balance(&num1, &num1) //Error 

/*②*/
struct Player {
    var name: String
  var health: Int
  var energy: Int
  mutating func shareHealth(with teamate: inout Player) {
    balance(&teamate.health, &health)
  }
}
var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria) //Ok
oscar.shareHealth(with: &oscar) //Error

/*③ 重叠访问结构体属性*/
var tulpe = (health: 10, energy: 20)
//Error
balance(&tulpe.health, &tulpe.energy)//重叠访问结构体的属性

var holly = Player(name: "Holly", health: 10, energy: 10)
// Error
balance(&holly.health, &holly.energy)//重叠访问结构体的属性

2-3、内存访问冲突

/*
如果下面的条件可以满足, 就说明重叠访问结构体的属性是安全的
    你访问的实例存储属性, 不是计算属性或者类属性
    结构体是局部变量而非全局变量
    结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
*/
// OK
func test() {
  var tulpe = (health: 10, energy: 20)
  balance(&tulpe.health, &tulpe.energy)

  var holly = Player(name: "Holly", health: 10, energy: 10)
  balance(&holly.health, &holly.energy)
}
test()

3-1、指针

/*
Swift中也有专门的指针类型, 这些都被定性为 "Unsafe"(不安全的), 常见的有以下4种类型
    UnsafePointer<Pointee> 类似于 const Pointee * //只通过指针可以访问内存, 不可以通过指针修改内存
    UnsafeMutablePointer<Pointee>  类似于 Pointee * //可以通过指针访问内存, 可以通过指针修改内存
    UnsafeRawPointer 类似于 const void * // 不确定指向什么类型的数据
    UnsafeMutableRawPointer 类型于 void *
*/
var age = 10
func test1(_ ptr: UnsafeMutablePointer<Int>) {// int *
  ptr.pointee += 20 //带泛型的指针类型, 直接通过.pointee就可以改变值
}
func test2(_ ptr: UnsafePointer<Int>) {// const int *
  print("test2", ptr.pointee)// 访问指针所指向内存的值
}
test2(&age) //10
test1(&age) //30
print(age) //30

var age2 = 10
func test3(_ ptr: UnsafeRawPointer) { // const void *
  print("test3", ptr.load(as: Int.self)) //加载
}
func test4(_ ptr: UnsafeMutableRawPointer) {// void *
  ptr.storeBytes(of: 30, as: Int.self) //存储
}
test3(&age2) //10
test4(&age2) //30
print(age2) //30

3-2、指针的应用示例

import Foundation
var arr = NSArray(object: 11, 22, 33, 44)
arr.enumerateObjects { (obj, idx, stop) in
    print(idx, obj)
  if idx == 2 { // 下标为2就停止遍历
    stop.pointee = true
  }
}

var arr = NSArray(object: 11, 22, 33, 44)
for (idx, onj) in arr.enumerated() {
    print(idx, obj)
  if idx == 2 { // 下标为2就停止遍历
    break
  }
}

3-3、获取指向某个变量的指针

import Foundation
var age = 10
var ptr = withUnsafeMutablePointer(to: &age) { $0 }

var ptr2 = withUnsafePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age){ (p) -> UnsafePointer<Int> in
    return p                                    
}
// 仿写底层实现上面👆
func withUnsafePointer<Result, T>(to: UnsafePointer<T>, body:(UnsafePointer<T>) -> Result) -> Result {
    return body(to)
})

/*总结:*/
var age = 11
var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
var ptr2 = withUnsafePointer(to: &age) { $0 }
ptr1.pointee = 22
print(ptr2.pointee) // 22
print(age) // 22

var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer( $0 ) }//自定义返回值
var ptr4 = withUnsafePointer( to: &age) { UnsafeRawPointer($0) }
ptr3.storeBytes(of: 33, as: Int.self)
print(ptr4.load(as: Int.self))// 33
print(age)//33

3-4、获得指向堆空间实例的指针

 class Person {
   var age: Int
   init(age: Int) {
     self.age = age
   }
 }
var peron = Person(age: 21)
var ptr1 = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
var personObjAddress = ptr1.load(as: UInt.self)
var ptr2 = UnsafeMutableRawPointer(bitPattern: personObjAddress)
print(ptr2) // 0x0000000100600fd80
print(Mems.ptr(ofRef: person))

/*总结:*/
class Person {}
var person = Person()
var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))
print(heapPtr!)

3-5、创建指针

import Foundation

var ptr = UnsafeRawPointer(bitPattern: 0x100001234)

var ptr = malloc(16) //创建指针: 分配堆空间16个字节大小
// 存
ptr?.storeBytes(of: 11, as: Int.self)
ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)
// 取
print((ptr?.load(as: Int.self))!) //11
print((ptr?.load(fromByteOffset: 8, as: Int.self))!) //22
free(ptr)//销毁: 释放堆空间

var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)//创建指针: 申请16个字节
ptr.storeBytes(of: 11, as: Int.self)//存储
ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)//存储
//取
print(ptr.load(as: Int.self)) //11
print(ptr.advanced(by: 8).load(as: Int.self))//22: 获取指向后8个字节的指针.取出其中的数据
ptr.deallocate()//销毁: 释放

------------------------------------------

var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)//创建指针, 分配3*(Int)8=24个空间
//ptr.initialize(repating: 10, count: 2)//初始化创建8*2=16个字节
ptr.initialize(to: 11)//初始化前八个字节
ptr.successor().initialize(to: 22)//.successor()跳8个字节
ptr.successor().successor().initialize(to: 33)//跳16个字节

print(ptr.pointee) // 11
// print(ptr.successor().pointee)//22
print((ptr + 1).pointee) // 22
// print(ptr.successor().successor().pointee)//33
print((ptr + 2).pointee) // 33
/// 等价于上面👆写法:
print(ptr[0]) // 11
print(ptr[1]) // 22
print(ptr[2]) // 33

ptr.deinitalize(count: 3)// 反初始化: 对应初始化
ptr.deallocate()// 销毁

-----------------------------------------------
class Person {
  var age: Int
  var name: String
  init(age: Int, name: String) {
    self.age = age
    self.name = name
  }
  deinit{ print(name, "deinit") }
}
var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
ptr.initialize(to: Person(age: 10, name: "jack"))
(ptr + 1).initialize(to: Person(age: 11, name: "Rose"))
(ptr + 1).initialize(to: Person(age: 12, name: "Kate"))
// Jakc deinit
// Rose deinit
// Kate deinit
ptr.deinitialize(count: 3)//反初始化多少个, 销毁多少个
ptr.deallocate()

3-6、指针之间的转换

var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)

ptr.assumingMemoryBound(to: Int.self).pointee = 11
(ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0 //非泛型ptr + 8是加8个字节. 如果是泛型ptr + 8是加8*(Int)8=64个字节

print(unsafeBitCase(ptr, to: UnsafePointer<Int>.self).pointee) //11 :将ptr强制转换成UnsafePointer<Int>.self)类型
print(unsafeBitCase(ptr + 8,to: UnsafePointer<Double>.self).pointee)//22.0

ptr.dellocate()

/*unsafeBitCase是忽略数据类型的强制转换, 不会因为数据类型的变化而改变原来的内存数据
    类似于C++中的reinterpret_cast.
*/
// 0x00 00 00 00 00 00 00 0A
var age = 10
//搜索: 浮点型的存储
// 0x40 24 00 00 00 00 00 00
//var age2 = Double(age)//这样转换数据结构会受影响的,会改变内存的存储格式,内存地址指向的8个字节发生变化.即存储在内存中的二进制值是不一样的

// 0x00 00 00 00 00 00 00 0A
var age1 = 10
// 0x00 00 00 00 00 00 00 0A
var age2 = UnsafeBitCast(age1, to: Double.self)
print(age2)// 5e-323 :科学技术法
// 内存地址指向的8个字节没有发生变化
// 不是强制转换成了二进制, 而是直接将二进制数据搬过去,只是类型发生了变化

3-7、补充UnsafeBitCast

import Foundation
class Person {}
var person = Person()//创建一个person对象,它的内存是在堆空间

// person就是指针变量, 只不过编译显示Person类型,不过它其实就是指针
// 那么我们真想做一个指向堆空间的指针,需要怎么做呢?
var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }//代表我们创建了一个指针,由于传入了person变量的地址值, ptr指向是person,并不是堆空间
var personObjectAddress = ptr.load(as: UInt.self)//取出存储空间的8个字节,即取出来person对象的地址值: 加载它所指向的存储空间,里面放着堆空间Person的地址值
//创建一个指针指向堆空间.相当于加载一下ptr指针所指向的存储空间里面所存储的东西
var ptr2 = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))//ptr2相当于指向堆空间的一个指针
print(ptr2)//打印出堆空间的地址值

/* 创建一个指向堆空间地址值指针的 简化版方法: unsafeBitCast( , to: ) */
var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self)
print(ptr)//打印出堆空间的地址值
//相当于ptr指向的就是堆空间的Person()对象
// 相当于把person变量内存里的数据直接搬到ptr,那么person变量里放着的就是Person()对象堆空间的地址值, 也就是Person()对象堆空间地址值交给ptr去存储, 只不过ptr强制变成指针类型.

var age1: Int = 10
var age2 = unsafeBitCase(age1, to:Double.self)//相当于将age1内存里的所有数据,也就是age1内存中存储的二进制数据是什么就直接赋值给age2. 相当于age2跟age1内存里的数据是百分百是一样的, 只不过类型不一样, age2变double类型

/*方法二:(略复杂)*/
var person = Person()
var address = unsafeBitCase(person, to: UInt.self)//先转换成一个地址值,一个整数(存储的就是堆空间的地址)
var ptr = UnsafeRawPointer(bitPattern: address)//再把地址值放进去转化成一个指针

第十七章 字面量协议、模式匹配、条件编译

1-1、字面量(Literal)

var age = 10
var isRed = false
var name = "Jack"
// 上面代码中的10、false、"Jack"就是字面量

/*
常见字面量的默认类型
    public typealias IntegerLiteralType = Int //整数类型默认字面量是Int
    public typealias FloatLiteralType = Double //浮点数的默认字面量是Double
    public typealias BooleanLiteralType = Bool 
    public typealias StringLiteralType = String 
*/

//可以通过typelias修饰字面量的默认类型
typealias FloatLiteralType = Float
typealias IntegerLiteralType = UInt8
var age = 10 // UInt8
var height = 1.68 // Float

/*Swift自带的绝大部分类型, 都支持直接通过字面量进行初始化
    Bool、Int、Float、Double、String、Array、Dictionary、Set、Optional等 
*/

1-2、字面量协议

/*Swift自带类型之所以能够通过字面量初始化, 是因为他们遵守了对应的协议
    Bool : ExpressibleByBooleanLiteral
    Int : ExpressibleByIntegerLiteral
    Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral
    Dictionary : ExpressibleByDictionaryLiteral
    String : ExpressibleByStringLiteral
    Array、Set : ExpressibleByArrayLiteral
    Optional : ExpressibleByNilLiteral
*/
var b: Bool = false // ExpressibleByBooleanLiteral
var i: Int = 10 // ExpressibleByIntegerLiteral
var f0: Float = 10 // ExpressibleByIntegerLiteral
var f1: Float = 10.0 // ExpressibleByFloatLiteral
var d0: Double = 10 // ExpressibleByIntegerLiteral
var d1: Double = 10.0 // ExpressibleByFloatLiteral
var s: String = "jack" // ExpressibleByStringLiteral
var arr: Array = [1, 2, 3] // ExpressibleByArrayLiteral
var set: Set = [1, 2, 3] // ExpressibleByArrayLiteral
var dict: Dictionary = ["Jack": 60] // ExpressibleByDictionaryLiteral
var o: Optional<Int> = nil  // ExpressibleByNilLiteral

1-3、字面量协议应用(一)

/* 想让某种类型想通过某种字面量来初始化的话, 那么就让某种类型遵守某种字面量协议 */
// 让Int类型遵守ExpressibleByBooleanLiteral协议, 就可以通过Bool类型字面量初始化Int类型
extension Int : ExpressibleByBooleanLiteral {
  public init(booleanLiteral value: Bool) { self = value ? 1 : 0 }
}
var num: Int = true
print(num) // 1

/* 有点类似于C++中的转换构造函数 */
/// 想通过一些字面量,直接初始化你自定义的一些类型, 那么遵守字面量协议就可以了
class Student : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, CustomStringConvertible {
    var name: String = ""
  var score: Double = 0
  required init(floatLiteral value: Double) { self.score = value }
  required init(integerLiteral value: Int) { self.score = Double(value) }
  
  required init(StringLiteral value: String) { self.name = value }//普通字符
  required init(unicodeScalarLiteral value: String) { self.name = value }//特殊符号
  required init(extendedGraphemeClusterLiteral value: String) { self.name = value }//特殊符号
  
  var description: String { "name=\(name),score=\(score)" }
}
var stu: Student = 90
print(stu) // name=,score=90.0
stu = 98.5
print(stu) // name=,scor=98.5
stu = "Jack"
print(stu) // name=Jack,scor=0.0

1-4、字面量协议应用(二)

struct Point {
  var x = 0.0, y = 0.0
}
extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
  init(arrayLiteral elements: Double...) {
    guard elements.count > 0 else { return }
    self.x = elements[0]
    guard elements.count > 1 else { return }
    self.y = elements[1]
  }
  init(dictionaryLiteral elements: (String, Double)...) {
    for (k, v) in elements {
      if k == "x" { self.x = v }
      else if k == "y" { self.y = v }
    }
  }
}
var p: Point = [10.5, 20.5]
print(p) // Point(x: 10.5, y: 20.5)
p = ["x" : 11, "y" : 22]
print(p) // Point(x: 11.0, y: 22.0)

2-1、模式(Pattern)

/*
什么是模式?
    模式是用于匹配的规则, 比如switch的case、捕捉错误的catch、if\guard\while\for语句的条件等.

Swift中的模式有:
    通配符模式 (Wildcard Pattern)
    标识符模式 (Identifier Pattern)
    值绑定模式 (Value-Binding Pattern)
    元组模式 (Tuple Pattern)
    枚举Case模式 (Enumeration Case Pattern)
    可选模式 (Optional Pattern)
    类型转换模式 (Type-Casting Pattern)
    表达式模式 (Expression Pattern)
*/

2-2、通配符模式 (Wildcard Pattern)

/*
    _  匹配任何值
    _? 匹配非nil值
*/
enum Life {
    case human(name: String, age: Int?)
  case animal(name: String, age: Int?)
}

func check(_ life: Life) {
  switch life {
  case .human(let name, _):
    print("human", name)
  case .animal(let name, _?):
    print("animal", name)
  default: 
    print("other")
  }
}
check(.human(name: "Rose", age: 20)) // human Rose
check(.human(name: "jack", age: nil)) // human Jack
check(.animal(name: "Dog", age: 5)) // animal Dog
check(.animal(name: "Cat", age: nil)) //other

2-3、标识符模式 (Identifier Pattern)

//给对应的变量、常量名赋值
var age = 10
let name = "jack"

2-4、值绑定模式 (Value-Binding Pattern)

let point = (3, 2)
switch point {
case let (x, y):
  print("The point is at(\(x),\(y)).")
}

2-5、元组模式 (Tuple Pattern)

let points = [(0, 0), (1, 0), (2, 0)]
for (x, _) in points {
  print(x)
}

let name: String? = "jack"
let age = 18
let info: Any = [1, 2]
switch (name, age, info) {
case (_?, _, _ as String):
  print("case")
defalut:
  print("default")
}// default

var scores = ["jack" : 98, "rose" : 100, "kate": 86]
for (name, score) in scores {
  print(name, score)
}

2-6、枚举Case模式 (Enumeration Case Pattern)

/* if case语句等价于只有1个case的switch语句 */
let age = 2
// 原来的写法
if age >= 0 && age <= 9 {
  print("[0, 9]")
}
// 枚举用例模式
if case 0...9 = age {//判断age是否在0..9区间内
  print("[0, 9]")
}
guard case 0...9 = age else { return }
print("[0, 9]")


switch age {
case 0...9: print("[0, 9]")
default: break
}

let ages: [Int?] = [2, 3, nil, 5]
for case nil in ages {//拿出数组ages里的每一个值,与case进行匹配,如果有nil就执行里面的语句
  print("有nil值")
  break
}// 有nil值


let points = [(1, 0), (2, 1), (3, 0)]
for case let (x, 0) in points {//匹配出points里面y为0的元素
  print(x)
}// 1 3

2-7、 可选模式 (Optional Pattern)

let age: Int? = 42
if case .some(let x) = age { print(x) }
if case let x? = age { print(x) }

let ages: [Int?] = [nil, 2, 3, nil, 5]
for case let age? in ages {//模式匹配: 非空则执行里面
  print(age)
}// 2 3 5
/// 跟上面👆的for, 效果是等价的
let ages: [Int?] = [nil, 2, 3, nil, 5]
for item in ages {//这样写不简洁, 不如上面的匹配模式
  if let age = item {
    print(age)
  }
} 

func check(_ num: Int?) {
  switch num {
  case 2?: print("2")//非空, 传2时执行
  case 4?: print("4")
  case 6?: print("6")
  case _?: print("other")//只要为非空, 都可以
  case _: print("nil")//不管是空还是非空,都可以
  }
}
check(4) // 4
check(8) // other
check(nil) // nil


/* 回忆补充: */
var age: Int? = 10
switch age {
case let x?:
  print(x)
case nil:
  print("nil")
}
//等价于👆
switch age {
case .some(let x):
  print(x)
case .none:
  print("nil")
}

2-8、类型转换模式 (Type-Casting Pattern)

let num: Any = 6
switch num {
case is Int:
    // 编译器依然认为num是Any类型
  print("is Int", num)
// case let n as Int://把n强转为int类型
// print("as Int", n + 1)
default:
  break
} 


class Animal { func eat() { print(type(of: self), "eat") } }
class Dog : Animal { func run() { print(type(of: self). "run") } }
class Cat : Animal { func jump() { print(type(of: self), "jump") } }
func check(_ animal: Animal) {
  switch animal {
  case let dog as Dog://判断能不能强转: 把dog强转为Dog类型, 不会对animal造成影响
    dog.eat()
    dog.run()
  case is Cat:// 判断是不是Cat类型
    animal.eat()
  default: break
  }
}
// Dog eat
// Dog run
check(Dog())
// Cat eat
check(Cat())

2-9、表达式模式 (Expression Pattern)

/* 表达式模式用在case中 */
let point = (1, 2)//元组
switch point {
case (0, 0):
  print("(0, 0) is at the origin.")
case (-2...2, -2...2):
  print("(\(point.0), \(point.1)) is near the origin.")
default:
  print("The point is at(\(point.0), \(point.1)).")
}// (1, 2) is near the origin.


/* 自定义表达式模式 一 */
/// 可以通过重载运算符, 自定义表达式模式的匹配规则
struct Struct {
    var score = 0, name = ""
  // pattern: case后面的内容, value: switch后面的内容
  static func ~= (pattern: Int, value: Student) -> Bool { value.score >= pattern }
  static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool { pattern.contains(value.score) }
  static func ~= (pattern: Range<Int>, value: Student) -> Bool { pattern.contains(value.score) }
}

var stu = Student(score: 75, name: "Jack")
switch stu {// 通过自定义表达式, 实现stu与范围值进行匹配
case 100: print(">= 100")
case 90: print(">= 90")
case 80..<90: print("[80, 90)")
case 60...79: print("[60, 70]")
case 0: print(">= 0")
default: break
}// [60, 79]

if case 60 = stu {
  print(">=60")
}// >=60

var info = (Student(score: 70, name: "Jack"), "及格")
switch info {
case let (60, text): print(text)
default: break
}// 及格

/* 自定义表达式模式 二 */
extension String {
    static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
    pattern(value)
  }
}

func hasPrefix(_ prefix: String) -> ((String) -> Bool) { { $0.hasPrefix(prefix) } }
func hasSuffix(_ suffix: String) -> ((String) -> Bool) { { $0.hasSuffix(suffix) } }

var str = "jack"
switch str {
case hasPrefix("j"), hasSuffix("k"): // (String) -> Bool
  print("以j开头, 以k结尾")
default: break
}// 以j开头, 以k结尾


/* 自定义表达式模式 三 */
func isEven(_ i: Int) -> Bool { i % 2 == 0 }
func isOdd(_i: Int) -> Bool { i % 2 != 0 }

extension Int {
  static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
        pattern(value)   
  }
}

var age = 9
switch age {
case isEvent://case后面放的是函数类型, 不是在调用函数
  print(age, "是个偶数")
case isOdd:
  print(age, "是个奇数")
default: break
}


/* 自定义表达式模式 四 */
prefix operator ~>
prefix operator ~>=
prefix operator ~<
prefix operator ~<=
prefix func ~> (_ i: Int) -> ((Int) -> Bool) { { $0 > i } }
prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { { $0 >= i } }
prefix func ~< (_ i: Int) -> ((Int) -> Bool) { { $0 < i } }
prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { { $0 <= i } }

var age = 9
switch age {
case ~>=0:
print("1")
case ~>10:
print("2")
default: break
} // [0, 10]

2-10、where

/* 可以使用where为模式匹配增加匹配条件 */
var data = (10, "Jack")
switch data {
case let (age, _) where age > 10:
print(data.1, "age>10")
case let (age, _) where age > 0:
print(data.1, "age>0")
default: break
}

var ages = [10, 20, 44, 23, 55]
for age in ages where age > 30 {
print(age)
} // 44 55

protocol Stackable { associatedtype Element }
protocol Container {
associatedtype Stack : Stackable where Stack.Element : Equatable
}

func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool
where S1.Element == S2.Element, S1.Element : Hashable {
return false
}

extension Container where Self.Stack.Element : Hashable { }

3-1、MARK、TODO、FIXME

// MARK: 类似于OC中的 #pragma mark
// MARK: - 类似于OC中的 #pragma mark -
// TODO: 用于标记未完成的任务
// FIXME: 用于标记待修复的问题

#warning("undo") //这个比较明显

3-2、条件编译

// 操作系统:macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD
#if os(macOS) || os(iOS)
// CPU架构:i386\x86_64\arm\arm64
#elseif arch(x86_64) || arch(arm64)
// swift版本
#elseif swift(<5) && swift(>=3)
// 模拟器
#elseif targetEnvironment(simulator)
// 可以导入某模块
#elseif canImport(Foundation)
#else
#endif


// debug模式
#if DEBUG

// release模式
#else

#endif

/* 可自定义: 需要在BuildSetting-->Swift Compiler - Custom Flags--> Active Compilation Conditions --> Debug 中添加例如"Test"就可以使用下面 */
#if TEST
print("test")
#endif
#if OTHER
print("other")
#endif

3-3、打印

func log<T>(_ msg: T,
            file: NSString = #file, //当前的函数环境所在文件(这里调用时,通过实参传递过来)
            line: Int = #line, //当前的函数环境所在行数
            fn: String = #function) { //当前的函数环境所在函数
    #if DEBUG
    let prefix = "\(file.lastPathComponent)_\(line)_\(fn):"
    print(prefix, msg)
    #endif
}

3-4、系统版本检测

if #available(iOS 10, macOS 10.12, *) {
  // 对于iOS平台,只在iOS10及以上版本执行
  // 对于macOS平台,只在macOS 10.12及以上版本执行
  // 最后的*表示在其他所有平台都执行
}

3-5、API可用性说明

@available(iOS 10, macOS 10.15, *)
class Person {}

struct Student {
  @available(*, unavailable, renamed: "study")//表明下面方法的方法名改变了
  func study_() {}
  func study() {}
  
  @available(iOS, deprecated: 11)//会提示下面的方法在iOS 11过期了
  @available(macOS, deprecated: 10.12)//会提示下面的方法在macOS 10.12过期了
  func run() {}
}
/* 更多用法参考 : https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
 */

var stu =  Student()
stu.run()//

第十八章 从OC到Swift

1-1、补充

/*
1. catch { error } // catch里面自带error
2. #warning("undo")
3. 编码习惯
*/

/// 写一个方法的时候, 一时半会不知道怎么写, 又不想让它报错, 可以用fatalError()占位
func test() -> Person {
    #warning("undo")
  fatalError()
}

2-1、iOS程序的入口

/*
在AppDelegate上面默认有个@UIApplicationMain标记,这表示
    编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理

也可以删掉@UIApplicationMain,自定义入口代码:新建一个main.swift文件
*/
import UIKit 
class MJApplication : UIApplication {}
UIApplicationMain(CommandLine.argc,
                 CommandLine.unsafeArgv,
                 NSStringFromClass(MJApplication.self),
                 NSStringFromClass(AppDelegate.self))

3-1、Swift调用OC

/*
新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h
        在Build Settings --> Objective-C Bridging Header -->写入桥接文件地址

在 {targetName}-Bridging-Header.h 文件中 #import OC需要暴露给Swift的内容
    #import "MJPerson.h"
*/

/// Swift调用OC – MJPerson.h
int sum(int a, int b);

@interface MJPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;

- (void)run;
+ (void)run;

- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end

/// Swift调用OC – MJPerson.m
@implementation MJPerson
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
    if (self = [super init]) {
        self.age = age;
        self.name = name;
    }
    return self;
}
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
    return [[self alloc] initWithAge:age name:name];
}

+ (void)run { NSLog(@"Person +run"); }
- (void)run { NSLog(@"%zd %@ -run", _age, _name); }

+ (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
- (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other); }
@end

int sum(int a, int b) { return a + b; }

/// Swift调用OC – Swift代码
var p = MJPerson(age: 10, name: "Jack")
p.age = 18
p.name = "Rose"
p.run() // 18 Rose -run
p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water

MJPerson.run() // Person +run
MJPerson.eat("Pizza", other: "Banana") // Person +eat Pizza Banana

print(sum(10, 20)) // 30 

/*
如果C语言暴露给Swift的函数名跟Swift中的其他函数名冲突了
    可以在Swift中使用 @_silgen_name 修改C函数名
*/
// C语言
int sum(int a, int b) {
        return a + b;
}
// Swift
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32 //给sum进行重命名swift_sum
print(swift_sum(10, 20)) // 30
print(sum(10, 20)) // 30

3-2、OC调用Swift

/*
Xcode已经默认生成一个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h

//在下面的路径可以看到
     在Build Settings --> Swift Compiler - General --> Objective-C Generated Interface Header Name --> xxxSwift.h
*/
import Foundation

@objcMembers class Car: NSObject {
  var price: Double
  var band: String
  init(price: Double, band: String) {
    self.price = price
    self.band = band
  }
  func run() { print(price, band, "run") }
  static func run() { print("Car run") }
}

extension Car {
  func test() { print(price, band, "test") }
}
/*
Swift暴露给OC的类最终继承自NSObject

使用@objc修饰需要暴露给OC的成员

使用@objcMembers修饰类
        代表默认所有成员都会暴露给OC(包括扩展中定义的成员)
        最终是否成功暴露,还需要考虑成员自身的访问级别
*/

/// OC调用Swift – {targetName}-Swift.h
// Xcode会根据Swift代码生成对应的OC声明,写入 {targetName}-Swift.h 文件

#import "TestSwift-Swift.h"
@interface Car : NSObject
@property (nonatomic) double price;
@property (nonatomic, copy) NSString * _Nonnull band;
- (nonnull instancetype)initWithPrice:(double)price band:(NSString * _Nonnull)band OBJC_DESIGNATED_INITIALIZER;
- (void)run;
+ (void)run;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end

@interface Car (SWIFT_EXTENSION(备课_Swift))
- (void)test;
@end

/// OC调用Swift – OC代码
#import "备课_Swift-Swift.h"
int sum(int a, int b) {
    Car *c = [[Car alloc] initWithPrice:10.5 band:@"BMW"];
    c.band = @"Bently";
    c.price = 108.5;
    [c run]; // 108.5 Bently run
    [c test]; // 108.5 Bently test
    [Car run]; // Car run
    return a + b;
}

/// OC调用Swift – @objc
// 可以通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)
@objc(MJCar)//暴露的同时进行重命名,比如此处将Car暴露成MJCar
@objcMembers class Car: NSObject {//此处必须继承自NSObject; @objcMembers 表示所有的成员都暴露给OC
    var price: Double
    @objc(name)//@objc 表示部分成员暴露给OC. 把band重命名为name
    var band: String
    init(price: Double, band: String) {
        self.price = price
        self.band = band
    }
    @objc(drive)
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
}
extension Car {
    @objc(exec:v2:)
    func test() { print(price, band, "test") }
}

MJCar *c = [[MJCar alloc] initWithPrice:10.5 band:@"BMW"];
c.name = @"Bently";
c.price = 108.5;
[c drive]; // 108.5 Bently run
[c exec:10 v2:20]; // 108.5 Bently test
[MJCar run]; // Car run

4-1、选择器(Selector)

/*Swift中依然可以使用选择器,使用#selector(name)定义一个选择器
  必须是被@objcMembers或@objc修饰的方法才可以定义选择器
*/
// Swift类要暴露给OC使用的话, 必须继承自NSObjct
@objcMembers class Person: NSObject {
    func test1(v1: Int) { print("test1") }
    func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
    func test2(_ v1: Double, _ v2: Double) { print("test2(_:_:)") }
    func run() {
        perform(#selector(test1))//执行方法
        perform(#selector(test1(v1:)))
        perform(#selector(test2(v1:v2:)))
        perform(#selector(test2(_:_:)))
        perform(#selector(test2 as (Double, Double) -> Void))
    }
}
/*
    1.为什么Swift暴露给OC的类最终要继承自NSObject?
        因为OC依赖于Runtime,Runtime要求类是有isa指针,isa指针必须继承自NSObject才有.

    2.oc里的方法拿到Swift里面调用, 底层是怎么调用的? 反过来, OC调用Swift底层又是如何调用?
    Swift调用OC写的方法, 走的是Runtime流程.
    OC调用Swift类, 走的也是Runtime流程
    
    3.如果Swift中给OC暴露了方法, 但是在Swift中调用了该方法, 那么底层是怎么调用的?
    是按照Swift的方式去调用的,没有必要走msg_send消息机制.从性能上讲比Runtime的性能要快一些, 因为Runtime那一套还要通过isa指针一层一层去找方法.

备注: 如果希望Swift中的方法必须走OC的Runtime机制的话, 需要在方法前加上 dynamic关键字.
*/

5-1、String

/// Swift的字符串类型String,跟OC的NSString,在API设计上还是有较大差异

// 空字符串
var emptyStr1 = ""
var emptyStr2 = String()

var str: String = "1"
// 拼接,jack_rose
str.append("_2")
// 重载运算符 +
str = str + "_3"
// 重载运算符 +=
str += "_4"
// \()插值
str = "\(str)_5"
// 长度,9,1_2_3_4_5
print(str.count)

var str = "123456"
print(str.hasPrefix("123")) // true
print(str.hasSuffix("456")) // true

5-2、String的插入和删除

var str = "1_2"
// 1_2_
str.insert("_", at: str.endIndex)
// 1_2_3_4
str.insert(contentsOf: "3_4", at: str.endIndex)
// 1666_2_3_4
str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
// 1666_2_3_8884
str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
// 1666hello_2_3_8884
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))

// 666hello_2_3_8884
str.remove(at: str.firstIndex(of: "1")!)
// hello_2_3_8884
str.removeAll { $0 == "6" }//发现字符是6的就删掉
var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
// hello_2_3_4
str.removeSubrange(range)//按范围删除

5-3、Substring

/* 
    String可以通过下标、 prefix、 suffix等截取子串,子串类型不是String,而是Substring 
*/

var str = "1_2_3_4_5"
// 1_2
var substr1 = str.prefix(3) 
// 4_5
var substr2 = str.suffix(3)//substr2作为指针指向同一块内存的指定区域
// 1_2
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
var substr3 = str[range] //Substring类型

// 最初的String,1_2_3_4_5
print(substr3.base)// 指向原来的字符串

// Substring -> String
var str2 = String(substr3)

/*
 Substring和它的base,共享字符串数据
 Substring发生修改 或者 转为String时,会分配新的内存存储字符串数据
*/

var str = "123456"
var substr = str[str.startIndex..<str.index(str.startIndex, offsetBy: 2)]
substr.append(contensOf: "666")
print(str, substr)// 123456 12666

5-4、String 与 Character

for c in "jack" { // c是Character类型
    print(c)
}

var str = "jack"
// c是Character类型
var c = str[str.startIndex]

5-5、String相关的协议

/* 
BidirectionalCollection 协议包含的部分内容
    startIndex 、 endIndex 属性、index 方法
    String、Array 都遵守了这个协议

RangeReplaceableCollection 协议包含的部分内容
    append、insert、remove 方法
    String、Array 都遵守了这个协议

Dictionary、Set 也有实现上述协议中声明的一些方法,只是并没有遵守上述协议
*/

5-6、多行String

let str = """
1
        "2"
3
        '4'
"""


// 如果要显示3引号,至少转义1个引号
let str = """
Escaping the first quote \"""
Escaping two quotes \"\""
Escaping all three quotes \"\"\"
"""

// 缩进以结尾的3引号为对齐线
let str = """
                1
                        2
        3
                4
        """


// 以下2个字符串是等价的
let str1 = "These are the same."
let str2 = """
These are the same.
"""

5-7、String 与 NSString

/*
String 与 NSString 之间可以随时随地桥接转换
     如果你觉得String的API过于复杂难用,可以考虑将String转为NSString
*/
var str1: String = "jack"
var str2: NSString = "rose"

var str3 = str1 as NSString
var str4 = str2 as String

// ja
var str5 = str3.substring(with: NSRange(location: 0, length: 2))
print(str5)

/*
比较字符串内容是否等价
  String使用 == 运算符
  NSString使用isEqual方法,也可以使用 == 运算符(本质还是调用了isEqual方法)
*/

6-1、Swift、OC桥接转换表

String ⇌ NSString
String ← NSMutableString
Array ⇌ NSArray
Array ← NSMutableArray
Dictionary ⇌ NSDictionary
Dictionary ← NSMutableDictionary
Set ⇌ NSSet
Set ← NSMutableSet

//👆上面显示的是能否通过as进行转换

第十九章 从OC到Swift、函数式编程

1-1、只能被class继承的协议

protocol Runnable1: AnyObject {}//方式一
protocol Runnable2: class {}//方式二
@objc protocol Runnable3 {}//方式三
/* 被 @objc 修饰的协议,还可以暴露给OC去遵守实现 */

1-2、可选协议

/* 可以通过 @objc 定义可选协议,这种协议只能被 class 遵守 */
@objc protocol Runnable {
    func run1()
    @objc optional func run2()// 这样也可以实现可选协议
    func run3()
}

class Dog: Runnable {
    func run3() { print("Dog run3") }
    func run1() { print("Dog run1") }
}
var d = Dog()
d.run1() // Dog run1
d.run3() // Dog run3

1-3、dynamic

/* 被 @objc dynamic 修饰的内容会具有动态性,比如调用方法会走runtime那一套流程 */
class Dog: NSObject {
    @objc dynamic func test1() {}//走runtime那一套流程
    func test2() {}//走虚表那一套流程
}
var d = Dog()
d.test1()
d.test2()

1-4、KVC\KVO

/*Swift 支持 KVC \ KVO 的条件:
    属性所在的类、监听器最终继承自 NSObject
    用 @objc dynamic 修饰对应的属性
*/
class Observer: NSObject {
    override func observeValue(forKeyPath keyPath: String?,
                                of object: Any?,
                                change: [NSKeyValueChangeKey : Any]?,
                                context: UnsafeMutableRawPointer?) {
         print("observeValue", change?[.newKey] as Any)
    }
}

class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observer: Observer = Observer()
    override init() {
        super.init()
        self.addObserver(observer,
                        forKeyPath: "age",
                        options: .new,
                        context: nil)
    }
    deinit {
        self.removeObserver(observer,
                            forKeyPath: "age")
    }
}
var p = Person()
// observeValue Optional(20)
p.age = 20
// observeValue Optional(25)
p.setValue(25, forKey: "age")

1-5、block方式的KVO

class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observation: NSKeyValueObservation?
    override init() {
        super.init()
        observation = observe(\Person.age, options: .new) {
            (person, change) in
            print(change.newValue as Any)
        }
    }
}
var p = Person()
// Optional(20)
p.age = 20
// Optional(25)
p.setValue(25, forKey: "age")

2-1、关联对象(Associated Object)

/*
在Swift中,class依然可以使用关联对象. 结构体和枚举不可以使用关联对象.

默认情况,extension不可以增加存储属性
     借助关联对象,可以实现类似extension为class增加存储属性的效果
*/
class Person {}
extension Person {
    private static var AGE_KEY: Void?// key名字随便取,只需保证唯一性. 类型属性只能通过类名去调用,所以要用大写的Self. 这个也是占一个字节.
    private static var WEIGHT_KEY = false//这个只是拿来用它们的地址值而已, 所以能省内存尽量省内存, 选用的bool类型, 它只占1个字节.
  
    var age: Int {
        get {
            (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self,//对象本身
                                    &Self.AGE_KEY,//地址值, 用来取出对应的newValue值
                                    newValue,
                                    .OBJC_ASSOCIATION_ASSIGN)
        }
    }
    var weight: Int {
        get {
            (objc_getAssociatedObject(self, &Self.WEIGHT_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self,
                                    &Self.WEIGHT_KEY,
                                    newValue,
                                    .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}

var p = Person()
print(p.age) // 0
p.age = 10
print(p.age) // 10

3-1、资源名管理

let img = UIImage(named: "logo")

let btn = UIButton(type: .custom)
btn.setTitle("添加", for: .normal)

performSegue(withIdentifier: "login_main", sender: self)


/*这种做法实际上是参考了Android的资源名管理方式*/
extension UIImage {
    convenience init?(_ name: R.image) {
    self.init(named: name.rawValue)
    }
}
extension UIViewController {
    func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
            performSegue(withIdentifier: identifier.rawValue, sender: sender)
    }
}
extension UIButton {
    func setTitle(_ title: R.string, for state: UIControl.State) {
            setTitle(title.rawValue, for: state)
    }
}


enum R {
    enum string: String {
            case add = "添加"
    }
    enum image: String {
            case logo
    }
    enum segue: String {
            case login_main
    }
}

let img = UIImage(R.image.logo)

let btn = UIButton(type: .custom)
btn.setTitle(R.string.add, for: .normal)

performSegue(withIdentifier: R.segue.login_main, sender: self)

3-2、资源名管理的其他思路

let img = UIImage(named: "logo")
let font = UIFont(name: "Arial", size: 14)


enum R {
    enum image {
            static var logo = UIImage(named: "logo")//静态属性
    }
    enum font {
        static func arial(_ size: CGFloat) -> UIFont? {
            UIFont(name: "Arial", size: size)
        }
    }
}
let img = R.image.logo
let font = R.font.arial(14)

/*更多优秀的思路参考:
https://github.com/mac-cain13/R.swift
https://github.com/SwiftGen/SwiftGen
*/

4-1、多线程开发 – 异步

/* //gcd
print(Thread.current, "main")
DispatchQueue.global().async {
    print(Thread.current, "async")
        DispatchQueue.main.async {
            print(Thread.current, "main")
    }
}
*/

/// 封装gcd
public typealias Task = () -> Void

public struct Asyncs {
    public static func async(_ task: @escaping Task) {
        _async(task)
    }
    public static func async(_ task: @escaping Task,
                            _ mainTask: @escaping Task) {
        _async(task, mainTask)
    }
    private static func _async(_ task: @escaping Task,
                              _ mainTask: Task? = nil) {
        let item = DispatchWorkItem(block: task)
        DispatchQueue.global().async(execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
    }
}

/// 使用:
Asyncs.async {
  print(1)//子线程
}

Asyncs.async({
  print(1, Thread.current)//子线程
}) {
    print(2, Thread.current)//主线程
}

4-2、多线程开发 – 延迟

/// 封装
public struct Asyncs {
    @discardableResult //这个关键字表示可以忽略下面方法的返回值.就是没有用到它的返回值也不会收到警告
    public static func delay(_ seconds: Double,
                            _ block: @escaping Task) -> DispatchWorkItem {
        let item = DispatchWorkItem(block: block)
        // 主线程延迟几秒执行
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds,
                                      execute: item)
        // 子线程延迟几秒执行
        //DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
        //                                                          execute: item)
        return item
    }
}

/// 使用:
private var item: DispatchWorkItem?

item = Asyncs.asyncDelay(3.0, { //定义任务
  print(1)
}){
  print(2)
}

item?.cancel()//可以随时取消任务

4-3、多线程开发 – 异步延迟

/// 封装
public struct Asyncs {
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task)
    }
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task,
                                  _ mainTask: @escaping Task) -> DispatchWorkItem {
        return _asyncDelay(seconds, task, mainTask)
    }
    private static func _asyncDelay(_ seconds: Double,
                                    _ task: @escaping Task,
                                    _ mainTask: Task? = nil) -> DispatchWorkItem {
        let item = DispatchWorkItem(block: task)
        DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
                                          execute: item)
        if let main = mainTask {
            item.notify(queue: DispatchQueue.main, execute: main)
        }
        return item
    }
}

4-4、多线程开发 – once

/*
 dispatch_once在Swift中已被废弃,取而代之
        可以用类型属性或者全局变量\常量
        默认自带 lazy + dispatch_once 效果
*/
/// 方法二: 只在文件中执行
fileprivate let initTask2: Void = {// 全局变量
        print("initTask2---------")
}()

class ViewController: UIViewController {
    // 方法一: 整个运行过程中只会初始化一次, 这里面的代码也只会执行一次
    // 它本质是全局变量,只初始化一次, 而且是lazy的, 用到的时候才初始化 
    static let initTask1: Void = {
            print("initTask1---------")
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        let _ = Self.initTask1
        let _ = initTask2
    }
}

4-5、多线程开发 – 加锁

/* gcd信号量 */
/// 比如做一个全局缓存类
class Cache {
    private static var data = [String: Any]()//全局变量
  
    private static var lock = DispatchSemaphore(value: 1)//允许同时最多只有1条线程访问它
    static func set(_ key: String, _ value: Any) {
            lock.wait()//加锁
            defer { lock.signal() } //解锁
      
            data[key] = value
    }
}

/* Foundation */
private static var lock = NSLock()
static func set(_ key: String, _ value: Any) {
        lock.lock()
        defer { lock.unlock() }
}

private static var lock = NSRecursiveLock()
static func set(_ key: String, _ value: Any) {
        lock.lock()
        defer { lock.unlock() }
}

5-1、Array的常见操作

var arr = [1, 2, 3, 4]

// [2, 4, 6, 8]
var arr2 = arr.map { $0 * 2 }

// [2, 4]
var arr3 = arr.filter { $0 % 2 == 0 }

// 10
// $0 上一次遍历返回的结果(初始值是0)
// $1 每次遍历到的数组元素
var arr4 = arr.reduce(0) { $0 + $1 }//设置初始值为0

// 10
var arr5 = arr.reduce(0, +)


func double(_ i: Int) -> Int { i * 2 } 
var arr = [1, 2, 3, 4]
// [2, 4, 6, 8]
print(arr.map(double))


/* 
var arr = Array.init(repeating: 4, count: 5)
print(arr) //[4, 4, 4, 4, 4]
*/
var arr = [1, 2, 3]
// [[1], [2, 2], [3, 3, 3]]
var arr2 = arr.map { Array.init(repeating: $0, count: $0) }
// [1, 2, 2, 3, 3, 3]
var arr3 = arr.flatMap { Array.init(repeating: $0, count: $0) }
/*flatMap会将里面的元素平铺出来,要求返回数组*/


var arr = ["123", "test", "jack", "-30"]
// [Optional(123), nil, nil, Optional(-30)]
var arr2 = arr.map { Int($0) }
// [123, -30]
func double(_ i: Int) -> Int { i * 2 } var arr3 = arr.compactMap { Int($0) }


// 使用reduce实现map、filter的功能
var arr = [1, 2, 3, 4]
// [2, 4, 6, 8]
print(arr.map { $0 * 2 })
print(arr.reduce([]) { $0 + [$1 * 2] })
// [2, 4]
print(arr.filter { $0 % 2 == 0 })
print(arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 })

5-2、lazy的优化

let arr = [1, 2, 3]
let result = arr.lazy.map {
    (i: Int) -> Int in
    print("mapping \(i)")
    return i * 2
}
print("begin-----")
print("mapped", result[0])//用到这个的时候才会映射
print("mapped", result[1])
print("mapped", result[2])
print("end----")
/*打印结果:
begin-----
mapping 1
mapped 2
mapping 2
mapped 4
mapping 3
mapped 6
end----
*/

5-3、可选类型Optional的map和flatMap

var num1: Int? = 10
// Optional(20)
var num2 = num1.map { $0 * 2 }//如果num1有值,会将num1先解包传入进行操作, map会将值再包装一层返回出去
var num3: Int? = nil
// nil
var num4 = num3.map { $0 * 2 }//有值才会执行里面代码, 否则直接返回nil


var num1: Int? = 10
// Optional(Optional(20))
var num2 = num1.map { Optional.some($0 * 2) }
// Optional(20)
var num3 = num1.flatMap { Optional.some($0 * 2) }//如果发现你里面本身就是可选类型,flatMap就不会再包装一层


var num1: Int? = 10
var num2 = (num1 != nil) ? (num1! + 10) : nil
var num3 = num1.map { $0 + 10 }
// num2、num3是等价的


var fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd"
var str: String? = "2011-09-10"
// old
var date1 = str != nil ? fmt.date(from: str!) : nil
// new
// var date1 = str.flatMap { fmt.date(from:$0) }//flatMap是传入一个函数,如果str有值的话, 它会将str的值传给这个函数, 现在由于这个函数是个闭包表达式, 所以传给函数的东西变成了$0,再执行函数,返回date
var date2 = str.flatMap(fmt.date)//现在直接将fmt.date这个函数传进去,那就代表会将str进行解包,解包之后直接传给fmt.date


var score: Int? = 98
// old
var str1 = score != nil ? "socre is \(score!)" : "No score"
// new
var str2 = score.map { "score is \($0)" } ?? "No score"


/* 根据person的名称找到person实例 */
struct Person {
        var name: String
        var age: Int
}
var items = [
        Person(name: "jack", age: 20),
        Person(name: "rose", age: 21),
        Person(name: "kate", age: 22)
]
/*查找数组索引:
  var items = [11, 22, 33]
  let index = items.firstIndex { $0 == 22 }
  print(index)
*/
// old
func getPerson1(_ name: String) -> Person? {
        let index = items.firstIndex { $0.name == name }
        return index != nil ? items[index!] : nil
}
// new
func getPerson2(_ name: String) -> Person? {
        return items.firstIndex { $0.name == name }.map { items[$0] }
}


/*字典转Person模型*/
struct Person {
    var name: String
    var age: Int
    init?(_ json: [String : Any]) {
        guard let name = json["name"] as? String,
                    let age = json["age"] as? Int else {
                return nil
        }
        self.name = name
        self.age = age
    }
}
var json: Dictionary? = ["name" : "Jack", "age" : 10]
// old
var p1 = json != nil ? Person(json!) : nil
// new
var p2 = json.flatMap(Person.init)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269