一、概述

本系列文章旨在温习Swift5中心语法且适当进行底层原理探求,归于阶段性温习和巩固,以供日后进一步探求Swift言语的底层原理做衬托。

整个系列文章如下,每一文章知识点独立成篇,欢迎各位按需或按爱好点击阅览:

  • 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版别、Swift编译原理】
  • 02-Swift5常用中心语法|基础语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard语句、区间】
  • 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
  • 04-Swift5常用中心语法|面向目标【特点、inout、类型特点、单例形式、办法、下标、承继、初始化】
  • 05-Swift5常用中心语法|高档语法【可选链、协议、错误处理、泛型、String与Array、高档运算符、扩展、拜访操控、内存办理、字面量、形式匹配】
  • 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、呼应式编程、Swift源码剖析】

二、从OC到Swift

1. 符号

咱们能够经过一些注释符号来特别标明注释的意义

  • // MARK: 相似OC中的#pragma mark
  • // MARK: - 相似OC中的#pragma mark -
  • // TODO: 用于符号未完成的任务
  • // FIXME: 用于符号待修正的问题

运用示例如下

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们还能够运用#warning来作为警告的提示,作用更为明显

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

2. 条件编译

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们还能够在Build Settings-> Swift Compiler -Custom Flags自界说符号

Other Swift Flags里自界说的符号要以-D开头

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

3. 打印

咱们能够自界说打印的内容,便于开发中的详情调查

/*
 * msg: 打印的内容
 * file: 文件名
 * line: 地点行数
 * fn: 履行的函数名
 */
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
}
func test() {
    log("哈哈")
} 
// 输出:
// main.swift_66_test(): 哈哈 

4. 系统的版别检测

if #available(iOS 10, macOS 10.12, *) {
    // 对于iOS渠道,只在iOS10及以上版别履行
    // 对于macOS渠道,只在macOS 10.12以上版别履行
    // 最后的*表明在其他一切渠道都履行
} 

5. API可用性说明

@available(iOS 10, macOS 10.12, *)
class Person {}
struct Student {
    // 旧的办法名更改,运用者用届时就会报错
    @available(*, unavailable, renamed: "study")
    func study_() {}
    func study() {}
    // 表明该办法在这个渠道已经过期
    @available(iOS, deprecated: 11)
    @available(macOS, deprecated: 10.12)
    func run() {}
} 

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

更多用法参阅:docs.swift.org/swift-book/…

6. 程序进口

AppDelegate上面默许有个@main符号,这表明编译器主动生成进口代码(main函数代码),主动设置AppDelegate为APP的署理

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

之前的Xcode版别会生成@UIApplicationMain符号,和@main的作用一样

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

也能够删掉@main或许@UIApplicationMain,自界说进口代码

1.创立main.swift文件

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

2.去掉AppDelegate里的符号

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

3.在main.swift里边自界说UIApplication并增加进口代码

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

7. Swift调用OC

假如咱们在Swift项目中需求调用到OC的代码,需求建立一个桥接头文件,文件名格式为{targetName}-Bridging-Header.h

在桥接文件里引证需求的OC头文件

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

Build Setting -> Swift Compiler - General中写好桥接文件途径

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

假如咱们是在Swift项目里第一次创立OC文件,Xcode会提示是否需求协助创立桥接文件

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

然后咱们就能够在Swift文件里调用OC的代码了

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

假如C言语露出给Swift的函数名和Swift中的其他函数名抵触了,能够在Swift中运用@_silgen_name修改C言语的函数名

// C文件
int sum(int a, int b) {
    return a + b;
}
// Swift文件
func sum(_ a: Int, _ b: Int) -> Int {
    a - b
}
@_silgen_name("sum")
func swift_sum(_ a: Int32, _ b: Int32) -> Int32
print(sum(5, 6))
print(swift_sum(5, 6)) 

@_silgen_name还能够用来调用C的私有函数

8. OC调用Swift

咱们要是想在OC文件中调用Swift代码,需求引证一个隐藏文件{targetName}-Swift.h

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

Swift露出给OC的类终究都要承继自NSObject

运用@objc润饰需求露出给OC的成员

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

运用@objcMembers润饰类,代表默许一切成员都会露出给OC(包括扩展中界说的成员)

终究是否成功露出,还需求考虑成员自身的权限问题

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们进入到test-Swift.h里看看编译器默许帮咱们转成的OC代码是怎样的

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们还能够经过@objc来对Swift文件里的类和成员重命名,来更适应于OC的代码标准

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

9. 选择器

Swift中依然能够运用选择器,运用#selector(name)界说一个选择器

有必要是被@objcMembers@objc润饰的办法才能够界说选择器

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

假如不加@objcMembers@objc是会报错的

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

10. 混编调用的实质

咱们先来思考一个问题,为什么Swift露出给OC的类终究要承继NSObject?

只要OC调用最后仍是走的音讯发送机制,要想能够完成音讯机制,就需求有isa指针,所以要承继NSObject

咱们在调用的当地打上断点,然后进行反汇编

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们发现,反汇编内部终究调用了objc_msgSend,很明显是音讯发送机制

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

那Swift调用OC的办法,是走的音讯发送机制,仍是Swift自身的调用办法呢?

咱们在调用的当地打上断点,然后进行反汇编

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们发现,反汇编内部终究调用了objc_msgSend,很明显是音讯发送机制

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

露出给OC运用的Swift函数和类,假如被Swift调用,是走的音讯发送机制,仍是Swift自身的调用办法呢?

咱们在调用的当地打上断点,然后进行反汇编

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们发现,反汇编内部是依照根据元类信息里的函数地址去调用的办法,没有Runtime相关的调用

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

咱们能够加上dynamic关键字,这样不管是OC调用仍是Swift调用都会走Runtime的音讯发送机制

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

反汇编之后

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

11. String

Swift的字符串类型String,和OC的NSString,在API规划上仍是有较大差异的

// 空字符串
var emptyStr1 = ""
var emptyStr2 = String() 
// 拼接字符串
var str: String = "1"
str.append("_2")
// 重载运算符
str = str + "_3"
str += "_4"
// 插值
str = "(str)_5"
print(str, str.count) // 1_2_3_4_5, 9 
// 字符串的判别
var str = "123456"
print(str.hasPrefix("123")) // true
print(str.hasSuffix("456")) // true 

11.1 String的刺进和删除

var str = "1_2"
str.insert("_", at: str.endIndex) // 1_2_
str.insert(contentsOf: "3_4", at: str.endIndex) // 1_2_3_4
str.insert(contentsOf: "666", at: str.index(after: str.startIndex)) // 1666_2_3_4
str.insert(contentsOf: "888", at: str.index(before: str.endIndex)) // 1666_2_3_8884
str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4)) // 1666hello_2_3_8884 
str.remove(at: str.firstIndex(of: "1")!) // 666hello_2_3_8884
str.removeAll { $0 == "6" } // hello_2_3_8884
let range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
str.removeSubrange(range) // hello_2_3_4 

11.2 Substring

String能够经过下标、prefix、suffix等截取子串,子串类型不是String,而是Substring

var str = "1_2_3_4_5"
var substr1 = str.prefix(3) // 1_2
var substr2 = str.suffix(3) // 4_5
var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
var substr3 = str[range] // 1_2
// 开始的String
print(substr3.base) // 1_2_3_4_5
// Substring -> String
var str2 = String(substr3) 
  • Substring和它的base,同享字符串数据
  • 其实质是Substring内部有一个指针指向String对应的区域
  • Substring产生修改或许转为String时,会分配新的内存存储字符串数据,不会影响到开始的String的内容,编译器会主动做优化
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

11.3 String与Character

for c in "jack" { // c是Character类型
    print(c)
} 
var str = "jack"
var c = str[str.startIndex] // c是Character类型 

11.4 String相关的协议

  • BidirectionalCollection协议包括的部分内容

    • startIndexendIndex特点、index办法
    • StringArray都恪守了这个协议
  • RangeReplaceableCollection协议包括的部分内容

    • appendinsertremove办法
    • StringArray都恪守了这个协议
  • DictionarySet也有完成上述协议中声明的一些办法,仅仅并没有恪守上述协议

11.5 多行String

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

假如要显示3引号,至少转义1个引号

let str = """
Escaping the first quote """
Escaping two quotes """
Escaping all three quotes """
""" 

以下两个字符是等价的

let str1 = "These are the same."
let str2 = """
These are the same.
""" 

缩进以结束的3引号为对齐线

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

11.6 String和NSString

  • StringNSString之间能够随时随地的桥接转化
  • 假如你觉得String的API过于复杂难用,能够考虑将String转为NSString
var str1: String = "jack"
var str2: NSString = "rose"
var str3 = str1 as NSString
var str4 = str2 as String
var str5 = str3.substring(with: NSRange(location: 0, length: 2))
print(str5) // ja 

咱们经过反汇编发现,StringNSString的转化会调用函数来完成的,相对会有性能的耗费,但由于编译器的优化,耗费的成本能够忽略不计

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

比较字符串内容是否等价

  • String运用==运算符
  • NSString运用isEqual办法,也能够运用==运算符(实质仍是调用了isEqual办法)
var str1: NSString = "jack"
var str2: String = "rose"
var str5: String = "rose"
var str6: NSString = "jack"
print(str2 == str5)
print(str1 == str6) 

经过反汇编,咱们能够看到==运算符的实质仍是调用了isEqual办法

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
!-w713

下面是Swift和OC的几个类型的转化表格

StringNSString能够彼此转化,而NSMutableString就只能单向转化成String

其他类型同理

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

12. 只能被class承继的协议

  • 假如协议对应AnyObject、class、@objc来润饰,那么只能被类所恪守
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
  • @objc润饰的协议,还能够露出给OC去恪守协议完成
// Swift文件
@objc protocol Runnable4 {
    func run()
}
// OC文件
@interface LLTest : NSObject<Runnable4>
@end
@implementation LLTest
- (void)run { }
@end 

能够经过@objc界说可选协议,这种协议只能被class恪守

@objc protocol Runnable4 {
    func run()
    @objc optional func eat()
}
class Person: Runnable4 {
    func run() {
        print("run")
    }
} 

13. dynamic

@objc dynamic润饰的内容会具有动态性,比方调用办法会走Runtime的音讯发送机制

class Dog {
    @objc dynamic func test1() {}
    func test2() {}
}
var d = Dog()
d.test1()
d.test2() 

具体报告调用进程能够参阅上文混编调用的实质

14. KVC、KVO

    1. Swift支撑KVC、KVO的条件需求特点地点的类、监听器终究承继自NSObject
    1. @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()
        addObserver(observer, forKeyPath: "age", options: .new, context: nil)
    }
    deinit {
        removeObserver(observer, forKeyPath: "age")
    }
}
var p = Person()
p.age = 20
p.setValue(25, forKey: "age")
// Optional(20)
// Optional(25) 
    1. block办法的`KVO
class Person: NSObject {
    @objc dynamic var age: Int = 0
    var observation: NSKeyValueObservation?
    override init() {
        super.init()
        observation = observe(\Person.age, options: .new, changeHandler: { (person, change) in
            print(change.newValue as Any)
        })
    }
}
var p = Person()
p.age = 20
p.setValue(25, forKey: "age")
// Optional(20)
// Optional(25) 

15. 相关目标(Associated Object)

  • 在Swift中,class依然能够运用相关目标
  • 默许情况下,extension不能够增加存储特点
  • 借助相关目标,能够完成相似extensionclass增加存储特点的作用
class Person {}
extension Person {
    // Void类型只占一个字节
    private static var AGE_KEY: Void?
    var age: Int {
        get {
            (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self, &Self.AGE_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN)
        }
    }
}
var p = Person()
print(p.age) // 0
p.age = 10
print(p.age) // 10 

16. 资源名办理

咱们日常在代码中对资源的运用如下

let img = UIImage(named: "logo")
let btn = UIButton(type: .custom)
btn.setTitle("增加", for: .normal)
performSegue(withIdentifier: "login_main", sender: self) 
  • 咱们选用枚举的办法对资源名进行办理
  • 这种办法是参阅了Android的资源名办理办法
enum R {
    enum string: String {
        case add = "增加"
    }
    enum image: String {
        case logo
    }
    enum segue: String {
        case login_main
    }
}
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let img = UIImage(named: R.image.logo)
        let btn = UIButton(type: .custom)
        btn.setTitle(R.string.add, for: .normal)
        performSegue(withIdentifier: R.segue.login_main, sender: self)
    }
}
extension UIImage {
    convenience init?(named 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)
    }
} 

资源名办理的其他思路

原始写法如下

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)
        }
    }
}
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let img = R.image.logo
        let font = R.font.arial(14)
    }
} 

更多优异的思路请参阅以下链接:

  • github.com/mac-cain13/…
  • github.com/SwiftGen/Sw…

17. 多线程开发

  • 运用DispatchWorkItem封装常用多线程履行函数
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)
    }
    /// 主线程延迟履行
    @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)
        return item
    }
    /// 异步延迟履行
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task) -> DispatchWorkItem {
        _asyncDelay(seconds, task)
    }
    @discardableResult
    public static func asyncDelay(_ seconds: Double,
                                  _ task: @escaping Task,
                                  _ mainTask: @escaping Task) -> DispatchWorkItem {
        _asyncDelay(seconds, task, mainTask)
    }
}
// MARK: - 私有API
extension Asyncs {
    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)
        }
    }
    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
    }
} 
  • dispatch_once在Swift中已被废弃,取而代之的是用类型特点或许全局变量\常量
  • 默许自带lazy+dispatch_once作用
fileprivate let initTask2: Void = {
    print("initTask2")
}()
class ViewController: UIViewController {
    static let initTask1: Void = {
        print("initTask1------------")
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        let _ = Self.initTask1
        let _ = initTask2
    }
} 
  • 多个线程操作同一份数据会有资源争夺问题,需求进行加锁
class Cache {
    private static var data = [String : Any]()
    private static var lock = DispatchSemaphore(value: 1)
    static func set(_ key: String, _ value: Any) {
        lock.wait()
        defer { lock.signal() }
        data[key] = value
    }
} 
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()
    }
}

三、函数式编程(Funtional Programming)

1. 根本概念

函数式编程(Funtional Programming,简称FP)是一种编程范式,也便是怎么编写程序的办法论

  • 主要思想:把计算进程尽量分解成一系列可复用函数的调用
  • 主要特征:”函数的第一等公民“,函数与其他数据类型一样的地位,能够赋值给其他变量,也能够作为函数参数、函数回来值

函数式编程最早出现在LISP言语,绝大部分的现代编程言语也对函数式编程做了不同程度的支撑,比方Haskell、JavaScript、Swift、Python、Kotlin、Scala

函数式编程中几个常用概念

  • Higher-Order Function、Function Currying
  • Functor、Applicative Functor、Monad

参阅资料:

  • adit.io/posts/2013-…
  • mokacoding.com/blog/functo…

2. Array的常见操作

var array = [1, 2, 3, 4]
// map:遍历数组,能够将每个元素对应做调整变成新的元素,放入新的数组中
var array2 = array.map { $0 * 2 } // [2, 4, 6, 8]
// filter:遍历数组,选出契合条件的元素放入新的数组中
var array3 = array.filter { $0 % 2 == 0 }
// reduce:首要设定一个初始值(0)
// $0:上一次遍历回来的成果(0,1,3,10)
//$1:每次遍历到的数组元素(1,2,3,4)
var array4 = array.reduce(0) { $0 + $1 } // 10
var array5 = array.reduce(0, +) // 同array4一样 
var array = [1, 2, 3, 4]
func double(_ i: Int) -> Int { i * 2 }
print(array.map(double)) // [2, 4, 6, 8]
print(array.map { double($0) }) // [2, 4, 6, 8]
    1. mapflatMap、compactMap的区别
    var arr = [1, 2, 3]
    var arr2 = arr.map { Array(repeating: $0, count: $0) } // [[1], [2, 2], [3, 3, 3]]
    // flatMap会将处理完的新元素都放在同一个数组中
    var arr3 = arr.flatMap { Array(repeating: $0, count: $0) } // [1, 2, 2, 3, 3, 3]
    
    var arr = ["123", "test", "jack", "-30"]
    var arr1 = arr.map { Int($0) } // [Optional(123), nil, nil, Optional(-30)]
    var arr2 = arr.compactMap { Int($0) } // [123, -30]
    var arr3 = arr.flatMap(Int.init)
    
    1. 运用reduce分别完成map、filter功用
    var arr = [1, 2, 3, 4]
    // map
    var arr1 = arr.map { $0 * 2 }
    print(arr1)
    var arr2 = arr.reduce([]) { $0 + [$1 * 2] }
    print(arr1)
    // filter
    var arr3 = arr.filter { $0 % 2 == 0 }
    print(arr3)
    var arr4 = arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 }
    print(arr4) 
    
    1. 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----- 
    
    1. OptionalmapflatMap
      会先将可选类型解包,处理完会再进行包装回来出去
    var num1: Int? = 10
    var num2 = num1.map { $0 * 2 } // Optional(20)
    var num3: Int? = nil
    var num4 = num3.map { $0 * 2 } // nil
    
    var num1: Int? = 10
    var num2 = num1.map { Optional.some($0 * 2) } // Optional(Optional(20))
    //flatMap发现其为可选项,不会再进行包装
    var num3 = num1.flatMap { Optional.some($0 * 2) } // Optional(20)
    var num4 = num1.flatMap { $0 * 2 } // Optional(20)
    
    var num1: Int? = 10
    var num2 = (num1 != nil) ? (num1! + 10) : nil // Optional(20)
    var num3 = num1.map { $0 + 10 } // Optional(20)
    
    var fmt = DateFormatter()
    fmt.dateFormat = "yyyy-MM-dd"
    var str: String? = "2011-09-10"
    var date1 = str != nil ? fmt.date(from: str!) : nil // Optional(2011-09-09 16:00:00 +0000)
    var date2 = str.flatMap(fmt.date) // Optional(2011-09-09 16:00:00 +0000)
    
    var score: Int? = 98
    var str1 = score != nil ? "score is (score!)" : "No score" // score is 98
    var str2 = score.map { "score is ($0)"} ?? "No score" // score is 98
    
    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)
    ]
    func getPerson1(_ name: String) -> Person? {
        // 遍历数组找到对应的索引
        let index = items.firstIndex { $0.name == name }
        return index != nil ? items[index!] : nil
    }
    func getPerson2(_ name: String) -> Person? {
        items.firstIndex { $0.name == name }
            .map { items[$0] }
    }
    let p1 = getPerson1("rose")
    let p2 = getPerson2("rose")
    
    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]
    var p1 = json != nil ? Person(json!) : nil // Optional(__lldb_expr_36.Person(name: "Jack", age: 10)) 
    var p2 = json.flatMap(Person.init) // Optional(__lldb_expr_36.Person(name: "Jack", age: 10))
    

3. 函数式的写法

  • 假如要完成以下功用: [(num + 3) * 5 - 1] % 10 / 2
  • 传统写法
var num = 1
func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
func multiple(_ v1: Int, _ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int, _ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int, _ v2: Int) -> Int { v1 % v2 }
divide(mod(sub(multiple(add(num, 3), 5), 1), 10), 2)
  • 函数式写法
func add(_ v: Int) -> (Int) -> Int {{ $0 + v }}
func sub(_ v: Int) -> (Int) -> Int {{ $0 - v }}
func multiple(_ v: Int) -> (Int) -> Int {{ $0 * v }}
func divide(_ v: Int) -> (Int) -> Int {{ $0 / v }}
func mod(_ v: Int) -> (Int) -> Int {{ $0 % v }}
infix operator >>> : AdditionPrecedence
func >>><A, B, C>(_ f1: @escaping (A) -> B,
                  _ f2: @escaping (B) -> C) -> (A) -> C {{ f2(f1($0)) }}
var fn = add(3) >>> multiple(5) >>> sub(1) >>> mod(10) >>> divide(2)
fn(num) 

4. 高阶函数(Higher-Order Function)

高阶函数至少满足下列一个条件的函数:

  • 接受一个或多个函数作为输入(map、filter、reduce等)
  • 回来一个函数

FP中处处都是高阶函数

5. 柯里化(Currying)

  • 什么是柯里化?
  • 将一个接受多参数的函数变换为一系列只接受单个参数的函数
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
  • Array、Optionalmap办法接纳的参数便是一个柯里化函数
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

演变示例

func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
add(2 + 4)
变为函数式的写法:
func add(_ v2: Int) -> (Int) -> Int {
    return { v1 in
        return v1 + v2
    }
}
add(4)(2)
再精简:
func add(_ v2: Int) -> (Int) -> Int {{ v1 in v1 + v2 }}
add(4)(2)
再精简:
func add(_ v: Int) -> (Int) -> Int {{ $0 + v }}
add(4)(2)
柯里化:
func currying<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) -> (A) -> C {{ b in { a in fn(a, b) }}}
let curriedAdd = currying(add)
print(curriedAdd(4)(2)) 
func add(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 }
add(2, 3, 5)
变为函数式的写法:
func add(_ v3: Int) -> (Int) -> (Int) -> Int {
    // v2是3
    return { v2 in
        // v1是2
        return { v1 in
            return v1 + v2 + v3
        }
    }
}
add(5)(3)(2)
再精简:
func add(_ v3: Int) -> (Int) -> (Int) -> Int {{ v2 in { v1 in v1 + v2 + v3 }}}
add(5)(3)(2)
柯里化:
func currying<A, B, C, D>(_ fn: @escaping (A, B, C) -> D) -> (C) -> (B) -> (A) -> D {{ c in { b in { a in fn(a, b, c) }}}}
let curriedAdd = currying(add)
print(curriedAdd(10)(20)(30)) 

一开始的示例就能够都保存旧的办法,然后经过柯里化来调用

func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 }
func multiple(_ v1: Int, _ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int, _ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int, _ v2: Int) -> Int { v1 % v2 }
prefix func ~<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) -> (A) -> C {{ b in { a in fn(a, b) }}}
infix operator >>> : AdditionPrecedence
func >>><A, B, C>(_ f1: @escaping (A) -> B,
                  _ f2: @escaping (B) -> C) -> (A) -> C {{ f2(f1($0)) }}
var num = 1
var fn = (~add)(3) >>> (~multiple)(5) >>> (~sub)(1) >>> (~mod)(10) >>> (~divide)(2)
fn(num) 

6. 函子(Functor)

  • Array、Optional这样支撑map运算的类型,称为函子(Functor)
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
    06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

下图充沛解说了函子

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

7. 适用函子(Applicative Functor)

对任意一个函子F,假如能支撑以下运算,该函子便是一个适用函子

func pure<A>(_ value: A) -> F<A>
func <*><A, B>(fn: F<(A) -> B>, value: F<A>) -> F<B>
  • Optional能够成为适用函子
func pure<A>(_ value: A) -> A? { value }
infix operator <*> : AdditionPrecedence
func <*><A, B>(fn: ((A) -> B)?, value: A?) -> B? {
    guard let f = fn, let v = value else { return nil }
    return f(v)
}
var value: Int? = 10
var fn: ((Int) -> Int)? = { $0 * 2 }
print(fn <*> value as Any) // Optional(20)
  • Array能够成为适用函子
func pure<A>(_ value: A) -> [A] { [value] }
infix operator <*> : AdditionPrecedence
func <*><A, B>(fn: [(A) -> B], value: [A]) -> [B] {
    var arr: [B] = []
    if fn.count == value.count {
        for i in fn.startIndex..<fn.endIndex {
            arr.append(fn[i](value[i]))
        }
    }
    return arr
}
print(pure(10)) // [10]
var arr = [{ $0 * 2 }, { $0 + 10 }, { $0 - 5 }] <*> [1, 2 , 3]
print(arr) // [2, 12, -2]

8. 单子(Monad)

对任意一共类型F,假如能支撑以下运算,那么就能够称为是一个单子

func pure<A>(_ value: A) -> F<A>
func flatMap<A, B>(_ value: F<A>, _ fn: (A) -> F<B>) -> F<B>

很显然,Array、Optional都是单子

四、面向协议编程(Protocol Oriented Programming)

1. 根本概念

面向协议编程(Protocol Oriented Programming,简称POP)

  • 是Swift的一种编程范式,Apple于2015年WWDC提出
  • 在Swift标准库中,能见到大量POP的影子 一起,Swift也是一门面向目标的编程言语(Object Oriented Programming,简称OOP)

在Swift开发中,OOPPOP是相得益彰的,任何一方并不能取代另一方

POP能补偿OOP一些规划上的缺乏

2. OOP和POP

回顾OOP

  • OOP的三大特性:封装承继多态
  • 承继的经典运用场合:
    当多个类(比方A、B、C类)具有很大共性时,能够将这些共性抽取到一个父类中(比方D类),最后A、B、C类承继D类

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

  • OOP的缺乏
  • 但有些问题,运用OOP并不能很好处理,比方怎么将BVC、DVC的公共办法run抽出来
class BVC: UIViewController {
    func run() {
        print("run")
    }
}
class DVC: UITableViewController {
    func run() {
        print("run")
    }
} 
  • 根据OOP想到的一些处理计划:

1.将run办法放到另一个目标A中,然后BVC、DVC拥有目标A特点

  • 多了一些额定的依赖联系

2.将run办法增加到UIViewController分类

  • UIViewController会越来越臃肿,并且会影响它的其他一切子类

3.将run办法抽取到新的父类,选用多承继(C++支撑多承继)

  • 会增加程序规划的复杂度,产生菱形承继等问题,需求开发者额定处理

POP的处理计划

protocol Runnable {
    func run()
}
extension Runnable {
    func run() {
        print("run")
    }
}
class BVC: UIViewController, Runnable {}
class DVC: UITableViewController, Runnable {} 

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

POP的注意点

  • 优先考虑创立协议,而不是父类(基类)
  • 优先考虑值类型(struct、enum),而不是引证类型(class)
  • 巧用协议的扩展功用
  • 不要为了面向协议而运用协议

3. POP的使用

下面咱们运用协议来完成前缀作用

var string = "123fdsf434"
protocol NameSpaceWrapperProtocol {
    associatedtype WrappedType
    var wrappedValue: WrappedType { get set }
    init(value: WrappedType)
}
struct NameSpaceWrapper<T>: NameSpaceWrapperProtocol {
    var wrappedValue: T
    init(value: T) {
        self.wrappedValue = value
    }
}
protocol NamespaceWrappable { }
extension NamespaceWrappable {
    var ll: NameSpaceWrapper<Self> {
        get { NameSpaceWrapper(value: self) }
        set {}
    }
    static var ll: NameSpaceWrapper<Self>.Type {
        get { NameSpaceWrapper.self }
        set {}
    }
}
extension NameSpaceWrapperProtocol where WrappedType: ExpressibleByStringLiteral {
    var numberCount: Int {
        (wrappedValue as? String)?.filter { ("0"..."9").contains($0) }.count ?? 0
    }
}
extension String: NamespaceWrappable {}
extension NSString: NamespaceWrappable {}
print(string.ll.numberCount)
print((string as NSString).ll.numberCount) // 6

运用协议完成类型判别

func isArray(_ value: Any) -> Bool { value is [Any] }
print(isArray([1, 2])) // true
print(isArray(["1", 2])) // true
print(isArray(NSArray())) // true
print(isArray(NSMutableArray())) // true
protocol ArrayType {}
func isArrayType(_ type: Any.Type) -> Bool { type is ArrayType.Type }
extension Array: ArrayType {}
extension NSArray: ArrayType {}
print(isArrayType([Int].self)) // true
print(isArrayType([Any].self)) // true
print(isArrayType(NSArray.self)) // true
print(isArrayType(NSMutableArray.self)) // true
print(isArrayType(String.self)) // false

五、呼应式编程(Reactive Programming)

1. 根本概念

呼应式编程(Reactive Programming,简称RP),是一种编程范式,于1997年提出,能够简化异步编程,供给更高雅的数据绑定

一般与函数式交融在一起,所以也会叫做:函数呼应式编程(Functional Reactive Programming,简称FRP)

比较闻名的、成熟的呼应式框架

  • ReactiveCocoa:简称RAC,有Objective-C、Swift版别
  • ReactiveX:简称Rx,有众多编程言语版别,比方RxJava、RxKotlin、RxJS、RxCpp、RxPHP、RxGo、RxSwift等

2. RxSwift

RxSwift(ReactiveX for Swift),ReactiveXSwift版别

源码: github.com/ReactiveX/R… beeth0ven.github.io/RxSwift-Chi…

六、Swift源码剖析

咱们经过剖析Swift标准库源码来更近一步了解Swift的语法

1.Array相关

map、filter的源码途径:/swift-main/stdlib/public/core/Sequence.swift

flatMap、compactMap、reduce的源码途径:/swift-main/stdlib/public/core/SequenceAlgorithms.swift

1.1 map

@inlinable
public func map<T>(
_ transform: (Element) throws -> T
) rethrows -> [T] {
  let initialCapacity = underestimatedCount
  var result = ContiguousArray<T>()
  result.reserveCapacity(initialCapacity)
  var iterator = self.makeIterator()
  // Add elements up to the initial capacity without checking for regrowth.
  for _ in 0..<initialCapacity {
    result.append(try transform(iterator.next()!))
  }
  // Add remaining elements, if any.
  while let element = iterator.next() {
    // 假如element是数组,会把整个数组作为元素加到新数组中
    result.append(try transform(element))
  }
  return Array(result)
} 

1.2 flatMap

@inlinable
public func flatMap<SegmentOfResult: Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
  var result: [SegmentOfResult.Element] = []
  for element in self {
    // 将数组元素增加到新数组中
    result.append(contentsOf: try transform(element))
  } 
  return result
} 

1.3 filter

@inlinable
public __consuming func filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {
  return try _filter(isIncluded)
}
@_transparent
public func _filter(
_ isIncluded: (Element) throws -> Bool
) rethrows -> [Element] {
  var result = ContiguousArray<Element>()
  var iterator = self.makeIterator()
  while let element = iterator.next() {
    if try isIncluded(element) {
      result.append(element)
    }
  }
  return Array(result)
} 

1.4 compactMap

@inlinable // protocol-only
public func compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
  return try _compactMap(transform)
}
@inlinable // protocol-only
@inline(__always)
public func _compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
  var result: [ElementOfResult] = []
  for element in self {
    // 会进行解包,只要不为空才会被加到数组中
    if let newElement = try transform(element) {
      result.append(newElement)
    }
  }
  return result
} 

1.5 reduce

@inlinable
public func reduce<Result>(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Element) throws -> Result
) rethrows -> Result {
  // 上一次的成果
  var accumulator = initialResult
  for element in self {
    accumulator = try nextPartialResult(accumulator, element)
  }
  return accumulator
} 

2. Substring相关

Substring的源码途径:/swift-main/stdlib/public/core/Substring.swift

2.1初始化

@frozen
public struct Substring: ConcurrentValue {
  @usableFromInline
  internal var _slice: Slice<String>
  @inlinable
  internal init(_ slice: Slice<String>) {
    let _guts = slice.base._guts
    let start = _guts.scalarAlign(slice.startIndex)
    let end = _guts.scalarAlign(slice.endIndex)
    // 保存传进来的字符串的内容和位置
    self._slice = Slice(
      base: slice.base,
      bounds: Range(_uncheckedBounds: (start, end))) 
    _invariantCheck()
  }
  @inline(__always)
  internal init(_ slice: _StringGutsSlice) {
    self.init(String(slice._guts)[slice.range])
  }
  /// Creates an empty substring.
  @inlinable @inline(__always)
  public init() {
    self.init(Slice())
  }
}
extension Substring {
  /// Returns the underlying string from which this Substring was derived.
  @_alwaysEmitIntoClient
  // _slice.base便是初始化传进来的字符串
  public var base: String { return _slice.base }
  @inlinable @inline(__always)
  internal var _wholeGuts: _StringGuts { return base._guts }
  @inlinable @inline(__always)
  // 从这儿也能看出和传进来的String共有的是同一块区域,在这块区域进行偏移获取Substring的内容
  internal var _offsetRange: Range<Int> {
    return Range(
      _uncheckedBounds: (startIndex._encodedOffset, endIndex._encodedOffset))
  }
  #if !INTERNAL_CHECKS_ENABLED
  @inlinable @inline(__always) internal func _invariantCheck() {}
  #else
  @usableFromInline @inline(never) @_effects(releasenone)
  internal func _invariantCheck() {
    // Indices are always scalar aligned
    _internalInvariant(
      _slice.startIndex == base._guts.scalarAlign(_slice.startIndex) &&
      _slice.endIndex == base._guts.scalarAlign(_slice.endIndex))
    self.base._invariantCheck()
  }
  #endif // INTERNAL_CHECKS_ENABLED
} 

2.2 append

extension Substring: RangeReplaceableCollection {
  @_specialize(where S == String)
  @_specialize(where S == Substring)
  @_specialize(where S == Array<Character>)
  public init<S: Sequence>(_ elements: S)
  where S.Element == Character {
    if let str = elements as? String {
      self.init(str)
      return
    }
    if let subStr = elements as? Substring {
      self = subStr
      return
    }
    self.init(String(elements))
  }
  // Substring的拼接
  @inlinable // specialize
  public mutating func append<S: Sequence>(contentsOf elements: S)
  where S.Element == Character {
		// 拼接时会创立一个新的字符串
    var string = String(self)
    self = Substring() // Keep unique storage if possible
    string.append(contentsOf: elements)
    self = Substring(string)
  }
} 

2.3 lowercased、uppercased

extension Substring {
  public func lowercased() -> String {
    return String(self).lowercased()
  }
  public func uppercased() -> String {
    return String(self).uppercased()
  }
  public func filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> String {
    return try String(self.lazy.filter(isIncluded))
  }
} 

3. Optional相关

Optional的源码途径:/swift-main/stdlib/public/core/Optional.swift

3.1 map

@inlinable
public func map<U>(
_ transform: (Wrapped) throws -> U
) rethrows -> U? {
  switch self {
  case .some(let y): // 先解包进行处理
    return .some(try transform(y)) // 然后再包装一层可选类型回来出去
  case .none:
    return .none
  }
} 

3.2flatMap

@inlinable
public func flatMap<U>(
_ transform: (Wrapped) throws -> U?
) rethrows -> U? {
  switch self {
  case .some(let y): // 先进行解包
    return try transform(y) // 将解包后的处理完直接给出去
  case .none:
    return .none
  }
} 

3.3 ==

  • ==两头都为可选项
@inlinable
public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
  switch (lhs, rhs) {
    case let (l?, r?):
      return l == r
    case (nil, nil):
      return true
    default:
      return false
  }
} 
  • ==左面为可选项,右边为nil
@_transparent
public static func ==(lhs: Wrapped?, rhs: _OptionalNilComparisonType) -> Bool {
  switch lhs {
    case .some:
      return false
    case .none:
      return true
  }
} 
  • ==左面为nil,右边为可选项
@_transparent
public static func ==(lhs: _OptionalNilComparisonType, rhs: Wrapped?) -> Bool {
  switch rhs {
    case .some:
      return false
    case .none:
      return true
  }
} 
  • _OptionalNilComparisonType是一个恪守ExpressibleByNilLiteral协议的结构体,能够用nil来进行初始化
// 恪守ExpressibleByNilLiteral协议的结构体,能够用nil来进行初始化
@frozen
public struct _OptionalNilComparisonType: ExpressibleByNilLiteral {
  /// Create an instance initialized with `nil`.
  @_transparent
  public init(nilLiteral: ()) {
  }
} 

3.4 ??

@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
    rethrows -> T {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
} 
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
    rethrows -> T? {
  switch optional {
  case .some(let value):
    return value
  case .none:
    return try defaultValue()
  }
} 

4. Metadata相关

4.1 了解Metadata

首要,咱们引进Apple的Metadata文档截图,了解一下 Metadata 是什么:

  • 06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】
  • 从上面附图中,咱们不难得出结论:
  • Swift 运行时 为程序中运用的每种数据类型(包括泛型类型的每个实例) 保存元数据(Metadata)记载
  • 换句话说,Metadata便是用于保存类型信息
  • (TODO:反射和)调试器东西能够运用这些元数据记载来了解有关类型的信息
    • 使用:字典转模型类的底层信息获取调试器调试类信息
  • 对于非泛型标称类型,这些元数据记载由编译器静态生成
  • 对于泛型类型的实例以及内部类型(例如元组、函数、协议组合等),元数据记载由运行时根据需求延迟创立
  • 每种类型都有唯一的元数据记载
  • 两个元数据指针值相等,当且仅当类型相等

4.2 Metadata通用信息布局

一切元数据记载同享一个公共标头,其间包括以下字段:

  • 虚函数表的地址值,供给根本操作,例如分配、复制和毁掉
  • 记载类型的巨细、对齐办法、步幅和其他根本特点
  • 记载指针坐落距元数据指针的偏移量 -1 处,即紧邻指针所引证地址之前的指针巨细的字。
  • kind 字段是一个指针巨细的整数
    • 描绘元数据所描绘的类型的种类
    • 该字段坐落元数据指针的偏移量 0 处

4.3 当前kind值与类型联系如下:

06-Swift5常用核心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码分析】

  • 类元数据的类型为 0,除非该类需求与 Objective-C 互操作。
    • 假如类需求与 Objective-C 互操作,则 kind 字段是指向 Objective-C 元类的 isa 指针。
    • 这样的指针能够与枚举元数据类型区别开来,因为它保证具有大于 2047 的值。
    • 请注意,这是比 @objc 特点意义更根本的互操作意义:
      • 它是支撑 Objective-C 所需的内容 音讯发送和保存/开释。
      • 在为 Apple 渠道构建时,一切类都需求在此级别上与 Objective-C 进行互操作。

4.4 简单介绍Class Metadata

源码途径:

  • /swift-main/include/swift/ABI/Metadata.h
  • /swift-main/include/swift/ABI/MetadataKind.def
  • /swift-main/include/swift/ABI/MetadataValues.h
  • /swift-main/include/swift/Reflection/Records.h

文档途径:

  • /swift-main/docs/ABI/TypeMetadata.rst

鄙人面的布局描绘中,相对于元数据指针的偏移量作为指针数组的索引给出。

  • 在32位渠道上,偏移1表明偏移4字节
  • 在64位渠道上,偏移1表明偏移8字节

Class Metadata

咱们能够从第三方库KaKaJSON中的ClassType,以及对应Metadata的相关文档来剖析Class Metadata信息

struct ClassLayout: ModelLayout {
    let kind: UnsafeRawPointer
    /// 指向父类类型的指针
    let superclass: Any.Type
    /// The cache data is used for certain dynamic lookups; it is owned by the runtime and generally needs to interoperate with Objective-C's use
    /// 缓存数据用于某些动态查找;它归于运行时,通常需求与Objective-C的运用进行互操作
    let runtimeReserved0: UInt
    let runtimeReserved1: UInt
    /// The data pointer is used for out-of-line metadata and is generally opaque, except that the compiler sets the low bit in order to indicate that this is a Swift metatype and therefore that the type metadata header is present
    let rodata: UInt
    /// Swift-specific class flags
    /// 类标志
    let flags: UInt32
    /// The address point of instances of this type
    /// 实例的地址值
    let instanceAddressPoint: UInt32
    /// The required size of instances of this type. 'InstanceAddressPoint' bytes go before the address point; 'InstanceSize - InstanceAddressPoint' bytes go after it
    /// 实例巨细
    let instanceSize: UInt32
    /// The alignment mask of the address point of instances of this type
    /// 实例对齐掩码
    let instanceAlignMask: UInt16
    /// Reserved for runtime use
    /// 运行时保存字段
    let reserved: UInt16
    /// The total size of the class object, including prefix and suffix extents
    /// 类目标的巨细
    let classSize: UInt32
    /// The offset of the address point within the class object
    /// 类目标地址
    let classAddressPoint: UInt32
    // Description is by far the most likely field for a client to try to access directly, so we force access to go through accessors
    /// An out-of-line Swift-specific description of the type, or null if this is an artificial subclass.  We currently provide no supported mechanism for making a non-artificial subclass dynamically
    var description: UnsafeMutablePointer<ClassDescriptor>
    /// A function for destroying instance variables, used to clean up after an early return from a constructor. If null, no clean up will be performed and all ivars must be trivial
    let iVarDestroyer: UnsafeRawPointer
    var genericTypeOffset: Int {
        let descriptor = description.pointee
        // don't have resilient superclass
        if (0x4000 & flags) == 0 {
            return (flags & 0x800) == 0
            ? Int(descriptor.metadataPositiveSizeInWords - descriptor.numImmediateMembers)
            : -Int(descriptor.metadataNegativeSizeInWords)
        }
        return GenenicTypeOffset.wrong
    }
}

关于MetaData的内容,若您感爱好,能够进一步进入官方文档了解:

Swift相关的官方文档

  • 1. Swift Documentation
  • 2. Type Metadata
  • 3. Swift Evolution

专题系列文章

1. 前知识

  • 01-探求iOS底层原理|综述
  • 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
  • 03-探求iOS底层原理|LLDB
  • 04-探求iOS底层原理|ARM64汇编

2. 根据OC言语探求iOS底层原理

  • 05-探求iOS底层原理|OC的实质
  • 06-探求iOS底层原理|OC目标的实质
  • 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
  • 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载进程、load 和 initialize 履行、相关目标
  • 09-探求iOS底层原理|KVO
  • 10-探求iOS底层原理|KVC
  • 11-探求iOS底层原理|探求Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的种类、内存办理、Block的润饰符、循环引证】
  • 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
  • 13-探求iOS底层原理|Runtime2【音讯处理(发送、转发)&&动态办法解析、super的实质】
  • 14-探求iOS底层原理|Runtime3【Runtime的相关使用】
  • 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
  • 16-探求iOS底层原理|RunLoop的使用
  • 17-探求iOS底层原理|多线程技能的底层原理【GCD源码剖析1:主行列、串行行列&&并行行列、全局并发行列】
  • 18-探求iOS底层原理|多线程技能【GCD源码剖析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
  • 19-探求iOS底层原理|多线程技能【GCD源码剖析2:栅门函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
  • 20-探求iOS底层原理|多线程技能【GCD源码剖析3:线程调度组dispatch_group、事件源dispatch Source】
  • 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
  • 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
  • 23-探求iOS底层原理|内存办理【Mach-O文件、Tagged Pointer、目标的内存办理、copy、引证计数、weak指针、autorelease

3. 根据Swift言语探求iOS底层原理

关于函数枚举可选项结构体闭包特点办法swift多态原理StringArrayDictionary引证计数MetaData等Swift根本语法和相关的底层原理文章有如下几篇:

  • 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版别、Swift编译原理】
  • 02-Swift5常用中心语法|基础语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard语句、区间】
  • 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
  • 04-Swift5常用中心语法|面向目标【特点、inout、类型特点、单例形式、办法、下标、承继、初始化】
  • 05-Swift5常用中心语法|高档语法【可选链、协议、错误处理、泛型、String与Array、高档运算符、扩展、拜访操控、内存办理、字面量、形式匹配】
  • 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、呼应式编程、Swift源码剖析】

其它底层原理专题

1. 底层原理相关专题

  • 01-计算机原理|计算机图形烘托原理这篇文章
  • 02-计算机原理|移动终端屏幕成像与卡顿

2. iOS相关专题

  • 01-iOS底层原理|iOS的各个烘托框架以及iOS图层烘托原理
  • 02-iOS底层原理|iOS动画烘托原理
  • 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
  • 04-iOS底层原理|因CPU、GPU资源耗费导致卡顿的原因和处理计划

3. webApp相关专题

  • 01-Web和类RN大前端的烘托原理

4. 跨渠道开发计划相关专题

  • 01-Flutter页面烘托原理

5. 阶段性总结:Native、WebApp、跨渠道开发三种计划性能比较

  • 01-Native、WebApp、跨渠道开发三种计划性能比较

6. Android、HarmonyOS页面烘托专题

  • 01-Android页面烘托原理
  • 02-HarmonyOS页面烘托原理 (待输出)

7. 小程序页面烘托专题

  • 01-小程序框架烘托原理