一、概述
本系列文章旨在温习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:
用于符号待修正的问题
运用示例如下
咱们还能够运用#warning
来作为警告的提示,作用更为明显
2. 条件编译
咱们还能够在Build Settings-> Swift Compiler -Custom Flags
自界说符号
在Other Swift Flags
里自界说的符号要以-D
开头
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() {}
}
更多用法参阅:docs.swift.org/swift-book/…
6. 程序进口
在AppDelegate
上面默许有个@main
符号,这表明编译器主动生成进口代码(main函数代码),主动设置AppDelegate
为APP的署理
之前的Xcode
版别会生成@UIApplicationMain
符号,和@main
的作用一样
也能够删掉@main
或许@UIApplicationMain
,自界说进口代码
1.创立main.swift
文件
2.去掉AppDelegate
里的符号
3.在main.swift
里边自界说UIApplication
并增加进口代码
7. Swift调用OC
假如咱们在Swift项目中需求调用到OC的代码,需求建立一个桥接头文件,文件名格式为{targetName}-Bridging-Header.h
在桥接文件里引证需求的OC头文件
在Build Setting -> Swift Compiler - General
中写好桥接文件途径
假如咱们是在Swift项目里第一次创立OC文件,Xcode
会提示是否需求协助创立桥接文件
然后咱们就能够在Swift文件里调用OC的代码了
假如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
Swift露出给OC的类终究都要承继自NSObject
运用@objc
润饰需求露出给OC的成员
运用@objcMembers
润饰类,代表默许一切成员都会露出给OC(包括扩展中界说的成员)
终究是否成功露出,还需求考虑成员自身的权限问题
咱们进入到test-Swift.h
里看看编译器默许帮咱们转成的OC代码是怎样的
咱们还能够经过@objc
来对Swift文件里的类和成员重命名,来更适应于OC的代码标准
9. 选择器
Swift中依然能够运用选择器,运用#selector(name)
界说一个选择器
有必要是被@objcMembers
或@objc
润饰的办法才能够界说选择器
假如不加@objcMembers
或@objc
是会报错的
10. 混编调用的实质
咱们先来思考一个问题,为什么Swift露出给OC的类终究要承继NSObject?
只要OC调用最后仍是走的音讯发送机制,要想能够完成音讯机制,就需求有isa指针
,所以要承继NSObject
咱们在调用的当地打上断点,然后进行反汇编
咱们发现,反汇编内部终究调用了objc_msgSend
,很明显是音讯发送机制
那Swift调用OC的办法,是走的音讯发送机制,仍是Swift自身的调用办法呢?
咱们在调用的当地打上断点,然后进行反汇编
咱们发现,反汇编内部终究调用了objc_msgSend
,很明显是音讯发送机制
露出给OC运用的Swift函数和类,假如被Swift调用,是走的音讯发送机制,仍是Swift自身的调用办法呢?
咱们在调用的当地打上断点,然后进行反汇编
咱们发现,反汇编内部是依照根据元类信息里的函数地址去调用的办法,没有Runtime
相关的调用
咱们能够加上dynamic
关键字,这样不管是OC调用仍是Swift调用都会走Runtime
的音讯发送机制
反汇编之后
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
的内容,编译器会主动做优化
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
协议包括的部分内容-
startIndex
、endIndex
特点、index
办法 -
String
、Array
都恪守了这个协议
-
-
RangeReplaceableCollection
协议包括的部分内容-
append
、insert
、remove
办法 -
String
、Array
都恪守了这个协议
-
-
Dictionary
、Set
也有完成上述协议中声明的一些办法,仅仅并没有恪守上述协议
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
-
String
和NSString
之间能够随时随地的桥接转化 - 假如你觉得
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
咱们经过反汇编发现,String
和NSString
的转化会调用函数来完成的,相对会有性能的耗费,但由于编译器的优化,耗费的成本能够忽略不计
比较字符串内容是否等价
-
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
办法
!-w713
下面是Swift和OC的几个类型的转化表格
String
和NSString
能够彼此转化,而NSMutableString
就只能单向转化成String
其他类型同理
12. 只能被class承继的协议
- 假如协议对应
AnyObject、class、@objc
来润饰,那么只能被类所恪守 - 被
@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
-
- Swift支撑
KVC、KVO
的条件需求特点地点的类、监听器终究承继自NSObject
- Swift支撑
-
- 用
@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)
-
-
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
不能够增加存储特点 - 借助相关目标,能够完成相似
extension
为class
增加存储特点的作用
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]
-
-
map
和flatMap、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)
-
-
- 运用
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)
- 运用
-
-
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-----
-
-
-
Optional
的map
和flatMap
会先将可选类型解包,处理完会再进行包装回来出去
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)
- 什么是柯里化?
- 将一个接受多参数的函数变换为一系列只接受单个参数的函数
-
Array、Optional
的map
办法接纳的参数便是一个柯里化函数
演变示例
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)
下图充沛解说了函子
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开发中,OOP
和POP
是相得益彰的,任何一方并不能取代另一方
POP
能补偿OOP
一些规划上的缺乏
2. OOP和POP
回顾OOP
-
OOP
的三大特性:封装
、承继、多态
- 承继的经典运用场合:
当多个类(比方A、B、C类)具有很大共性时,能够将这些共性抽取到一个父类中(比方D类),最后A、B、C类承继D类
- 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 {}
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),ReactiveX
的Swift
版别
源码: 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 是什么:
- 从上面附图中,咱们不难得出结论:
Swift 运行时 为程序中运用的每种数据类型(包括泛型类型的每个实例) 保存元数据(Metadata)记载
- 换句话说,Metadata便是用于保存类型信息
- (TODO:
反射
和)调试器东西
能够运用这些元数据记载来了解有关类型的信息- 使用:
字典转模型
、类的底层信息获取
、调试器调试类信息
等
- 使用:
- 对于
非泛型
标称类型,这些元数据记载由编译器静态生成 - 对于
泛型
类型的实例以及内部类型(例如元组、函数、协议组合等),元数据记载由运行时根据需求延迟创立 - 每种类型都有唯一的元数据记载;
- 两个元数据指针值相等,当且仅当类型相等
4.2 Metadata通用信息布局
一切元数据记载同享一个公共标头,其间包括以下字段:
- 虚函数表的地址值,供给根本操作,例如分配、复制和毁掉
- 记载类型的巨细、对齐办法、步幅和其他根本特点
- 记载指针坐落距元数据指针的偏移量 -1 处,即紧邻指针所引证地址之前的指针巨细的字。
-
kind 字段
是一个指针巨细的整数- 描绘元数据所描绘的类型的种类
- 该字段坐落元数据指针的偏移量 0 处
4.3 当前kind值与类型联系如下:
- 类元数据的类型为 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多态原理
、String
、Array
、Dictionary
、引证计数
、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-小程序框架烘托原理