iOS-Swift-标准库源码分析+项目实战

一. Swift源码简介

Swift于2015年正式开源,github地址: https://github.com/apple/swift

几个可能会经常看的目录:
docs:一些文档
stdlib:Swift源码
lib:C++源码
include:C++头文件

标准库源码位置:https://github.com/apple/swift/tree/master/stdlib/public/core

1. Array分析

https://github.com/apple/swift/blob/master/stdlib/public/core/Sequence.swift

① 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() { //利用迭代器遍历数组中的元素
        result.append(try transform(element)) //拿到元素,传到闭包里面,闭包表达式返回的结果放到数组里面
    }
    return Array(result) //返回结果数组
}

② flatMap

@inlinable
public func flatMap<SegmentOfResult: Sequence>(
    _ transform: (Element) throws -> SegmentOfResult
    ) rethrows -> [SegmentOfResult.Element] {
    var result: [SegmentOfResult.Element] = []
    for element in self { //遍历数组中的每一个元素
        //将数组元素传给闭包表达式,调用闭包表达式,将闭包表达式的返回结果拼接到数组中
        //通过contentsOf可以看出,如果返回结果是数组,会将数组的内容取出来拼接到数组中,这也是flatMap和map的区别
        result.append(contentsOf: try transform(element)) 
    }
    return result
  }
}

③ 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) { //拿到元素,传到闭包里面,调用闭包,返回true就把元素放到数组里面去
            result.append(element) //返回数组
        }
    }
    
    return Array(result)
}

https://github.com/apple/swift/blob/master/stdlib/public/core/SequenceAlgorithms.swift

④ reduce

@inlinable
public func reduce<Result>(
    into initialResult: __owned Result,
    _ updateAccumulatingResult:
    (_ partialResult: inout Result, Element) throws -> ()
    ) rethrows -> Result {
    var accumulator = initialResult
    for element in self { //遍历元素
        //将元素和上一次的结果传进来,调用闭包表达式,返回一个结果,再把这个结果传给下一个闭包
        try updateAccumulatingResult(&accumulator, element) 
    }
    return accumulator
  }
}

⑤ compactMap

@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
  }
}

2. Substring分析

https://github.com/apple/swift/blob/master/stdlib/public/core/Substring.swift

① append

@inlinable // specialize
public mutating func append<S: Sequence>(contentsOf elements: S)
    where S.Element == Character {
        var string = String(self) //将Substring传进去创建一个新的string
        self = Substring() // Keep unique storage if possible
        string.append(contentsOf: elements) //再用新的string拼接
        self = Substring(string) //再将新的string转成Substring
  }
}

② lowercased、uppercased

public func lowercased() -> String {
    return String(self).lowercased() //先转成String,再调用lowercased()方法
}

public func uppercased() -> String {
    return String(self).uppercased()
}

3. Optional分析

https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift

① map、flatMap

@inlinable
public func map<U>(
    _ transform: (Wrapped) throws -> U
    ) rethrows -> U? {
    switch self {
    //如果不为nil,就解包将解包之后的值传入闭包表达式。再将闭包表达式的返回值包装成可选类型,然后返回出去
    case .some(let y):
        return .some(try transform(y))
    case .none: //如果为nil就返回nil
        return .none
    }
}

@inlinable
public func flatMap<U>(
    _ transform: (Wrapped) throws -> U?
    ) rethrows -> U? {
    switch self {
    case .some(let y):
        //直接返回,并没有包装成可选类型(区别:map会包装一层可选类型,flatMap不会包装)
        return try transform(y) 
    case .none:
        return .none
    }
}

② 等价运算符 ==

关于等价运算符可以参考之前的文章iOS-Swift-高级运算符

extension Optional where Wrapped: Equatable {
    //左边为nil右边为可选类型会进入这里
    public static func ==(lhs: _OptionalNilComparisonType, rhs: Wrapped?) -> Bool {
        switch rhs {
        case .some:
            return false
        case .none:
            return true
        }
    }
    //左边为可选类型右边为nil会进入这里
    public static func ==(lhs: Wrapped?, rhs: _OptionalNilComparisonType) -> Bool {
        switch lhs {
        case .some:
            return false
        case .none:
            return true
        }
    }
    //左右都为可选类型会进入这里(如果有非可选的会被包装成可选的,如:Int->Int?)
    //如果两个都为可选类型,进入这个方法的时候会把两个参数包装成?多的那个
    public static func ==(lhs: Wrapped?, rhs: Wrapped?) -> Bool {
        switch (lhs, rhs) {
        case let (l?, r?): //左右都不为nil 解包,将解包后的值赋值给l和r
            return l == r //l和r相等就返回true
        case (nil, nil): //左右都为nil
            return true
        default: //其他情况
            return false
        }
    }
}

var age1: Int???? = 20
var age2: Int = 20
print(age1 == age2) //true 都会包装成Int????

var age3: Int? = nil
var age4: Int??? = age3
var age5: Int??? = nil
print(age4 == age5) //false age4和age5不一样,age5盒子里就一个nil,age4盒子里不为nil,装了个age3,age3里面才是nil,所以他们不相等

var age6: Int??? = nil
var age7: Int? = nil
print(age6 == age7) //false 这时候比较的时候会进入default,一个为nil一个不为nil,换个思路,类型不一样肯定不相等啊

③ 空合并运算符 ??

关于空合并运算符可以参考之前的文章iOS-Swift-可选项

extension Optional where Wrapped: Equatable {
    //左边为T?右边为T会调用这个
    public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
        rethrows -> T {
            switch optional {
            case .some(let value):
                return value
            case .none:
                return try defaultValue()
            }
    }
    
    //左边右边都为T?会调用这个
    public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
        rethrows -> T? {
            switch optional {
            case .some(let value):
                return value
            case .none:
                return try defaultValue()
            }
    }
}

var age1: Int? = 10
var age2: Int? = 20
print(age1 ?? age2)
//这时候T?就是Int?,T就是Int
//会调用第二个,最后返回值是T?类型,所以返回Optional(10)

var age3: Int??? = 10
var age4: Int? = 20
print(age3 ?? age4)
//这时候T?就是Int???,T就是Int??
//会调用第一个,最后返回值是T类型,所以返回(Optional(Optional(10)))

var age5: Int??? = nil
var age6: Int? = 20
print(age5 ?? age6)
//这时候T?就是Int???,T就是Int??
//会调用第一个,最后返回值是T类型,所以返回(Optional(Optional(20)))

4. Metadata分析

Swift的类内存结构是:
前8个字节放metadata类型相关,后8个字节放指针相关,后⾯再放成员变量信息。
如果继承于NSObject,内存信息就变成了:前8个字节放isa指针相关,后⾯再放成员变量信息。

那么metadata里面放的是什么呢?
metadata里面放的东西跟ABI有关,如果ABI稳定了,metadata⾥⾯的东西就定型了。Swift5之后ABI就稳定了,所以metadata里面放的东西不会有大改变了。

metadata文档如下:
文档:https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst

其他参考:
https://github.com/apple/swift/blob/master/include/swift/ABI/Metadata.h https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataKind.def https://github.com/apple/swift/blob/master/include/swift/ABI/MetadataValues.h https://github.com/apple/swift/blob/master/include/swift/Reflection/Records.h

metadata有什么用呢?
通过metadata就能找到这个类型中有哪些成员,以前OC是通过Runtime的class_copyIvarList获取的,但是纯Swift类不⽀持,Swift是通过metadata获取的,之后就能进⾏JSON<->Model互转(例如MJ老师的KakaJSON)。

关于KakaJSON:
根据上面的metadata文档也可以弄清楚metadata里面的布局,但是比较麻烦。MJ老师的KakaJSON框架的Metadata->Layout文件夹下面存放的就是Metadata的布局,更直观。

5. 反射

反射是编程语言中一项强大的能力,比如Java语言的反射机制。

  • 对于任意一个类型,都能够动态获取这个类的所有属性和方法信息。
  • 对于任意一个实例,都能够动态获取、调用它的任意属性和方法。

Swift的反射机制目前还比较弱,通过Mirror类型来提供简单的反射功能

struct Person {
    var age: Int = 0
    var name: String = ""
}

let mirror = Mirror(reflecting: Person(age: 10, name: "Jack"))
// struct
print(mirror.displayStyle!) //是结构体还是类
// Person
print(mirror.subjectType) //名称是什么
// nil
print(mirror.superclassMirror as Any) //父类的Mirror是什么
// age 10
// name Jack
for case let (label?, value) in mirror.children { //遍历属性
    print(label, value)
}

如果Swift的反射机制已经很强大了,MJ老师的KakaJSON框架就不用自己写Metadata的布局了,直接使用Mirror类型就可以知道类型中有哪些成员了。

二. 项目实战

1. 常用Swift第三方库

网络请求:https://github.com/Alamofire/Alamofire
图片下载:https://github.com/onevcat/Kingfisher
JSON访问:https://github.com/SwiftyJSON/SwiftyJSON
JSON-Model转换:https://github.com/kakaopensource/KakaJSON

补充:
Alamofire的request方法可以直接使用,不像以前需要创建实例,这是为什么呢?
因为request是public的,其他模块也可以使用。

如果自己也有request方法,想要使用Alamofire的request方法,就直接Alamofire.request就可以了,所以现在好多第三方库的方法都是直接public的,也不怕重名。

2. Kingfisher注意点

Kingfisher默认不支持WebP格式的图片,需要额外安装KingfisherWebP

pod 'KingfisherWebP'
iconView.kf.setImage(with: URL(string: user.thumb),
                     options: [.processor(WebPProcessor.default), .cacheSerializer(WebPSerializer.default)])

3. 库的导入问题

默认情况下,用到哪个库就要导入哪个库,无疑增加了很多重复的工作量,如何办到全局导入库?

新建一个用于Swift调用OC的桥接文件:targetName-Bridging-Header.h

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

推荐阅读更多精彩内容