序列 Sequence
序列協(xié)議是集合類型結(jié)構(gòu)中的基礎(chǔ)。
序列代表一系列類型相同的元素杯聚,你可以對(duì)這些元素進(jìn)行迭代
Sequence協(xié)議
Sequence協(xié)議是集合類型的基礎(chǔ)臼婆,Swift中Sequence協(xié)議為序列提供了迭代的能力。Sequence 協(xié)議只要求實(shí)現(xiàn)makeIterator()方法,該方法返回一個(gè)迭代器Iterator械媒;
public protocol Sequence {
// 元素類型
associatedtype Element
// 迭代器類型 == 元素類型
associatedtype Iterator: IteratorProtocol where Iterator.Element == Element
//迭代器
__consuming func makeIterator() -> Iterator
//...
}
迭代器
序列通過(guò)創(chuàng)建一個(gè)迭代器來(lái)提供對(duì)元素的訪問(wèn)目锭。迭代器每次產(chǎn)生一個(gè)序列的值评汰,并且當(dāng)遍歷序列時(shí)對(duì)遍歷狀態(tài)進(jìn)行管理纷捞。在 IteratorProtocol 協(xié)議中唯一的一個(gè)方法是 next(),這個(gè)方法需要在每次被調(diào)用時(shí)返回序列中的下一個(gè)值被去。當(dāng)序列被耗盡時(shí)主儡,next() 返回 nil
public protocol IteratorProtocol {
/// The type of element traversed by the iterator.
associatedtype Element
mutating func next() -> Element?
}
for循環(huán)的背后是編譯器創(chuàng)建了一個(gè)迭代器,然后不斷的調(diào)用next(),直到返回nil
// 猜測(cè)編譯器實(shí)現(xiàn) for
var iter = CustomIterator()
while let x = iter.next(){
//...
}
例子
1.自定義迭代器類型惨缆,遵守IteratorProtocol協(xié)議糜值,不需要指明Element類型丰捷,編譯器會(huì)從next的返回類型推斷出Element的類型
自定義序列,遵守Sequence協(xié)議寂汇,同樣不需要指明Element類型病往,編譯器會(huì)從makeIterator的類型中推斷出Element類型。
最后通過(guò)for-in 就能不斷打印結(jié)果了
struct customProtocol:IteratorProtocol{
//Element 可以省略骄瓣,編譯器會(huì)從next的返回類型推斷出Element的類型
//typealias Element = Int
func next() -> Int? {
return 1
}
}
//...
struct customSequence: Sequence{
//同樣停巷,編譯器會(huì)從makeIterator的類型中推斷出Element類型,不需要再次指明
func makeIterator() -> some IteratorProtocol {
customProtocol()
}
}
//...
var customPrint = customSequence()
for i in customPrint{
print(i) // 由于next返回1榕栏,所以這里會(huì)無(wú)限打印
}
2.自定義反轉(zhuǎn)一個(gè)迭代器
// 先定義一個(gè)實(shí)現(xiàn)IteratorProtocol 的類型
struct ReverseIterator<T>:IteratorProtocol{
typealias Element = T;
var arr : [Element];
var idx = 0;
init(arr:[Element]) {
self.arr = arr;
idx = arr.count - 1;
}
mutating func next() -> T? {
if idx < 0 {
return nil;
}else{
let ele = arr[idx];
idx = idx - 1;
return ele;
}
}
}
// 再來(lái)定義sequence
struct ReverceSequence<T>:Sequence{
var arr:[T];
init(arr:[T]) {
self.arr = arr;
}
__consuming func makeIterator() -> ReverseIterator<T> {
return ReverseIterator(arr: self.arr);
}
}
//
let array = [2,5,8,10];
for i in ReverceSequence(arr: array){
// 10 8 5 2
print(i, separator: "-", terminator: "-");
}
Lazy變量
惰性變量是按需初始化的存儲(chǔ)屬性畔勤,只能在struct或class中使用惰性變量。
例如扒磁,創(chuàng)建一個(gè)帶有惰性變量的Person結(jié)構(gòu)來(lái)計(jì)算BMI:
struct Person {
var weight: Double
var height: Double
lazy var BMIIndex: Double = {
return weight / pow(height, 2)
}()
}
///當(dāng)初始化Person對(duì)象時(shí)庆揪,BMI不會(huì)自動(dòng)計(jì)算。而是在第一次引用的時(shí)候才計(jì)算
var jack = Person(weight: 90, height: 120)
print(jack.BMIIndex)
Lazy Sequences
在Swift標(biāo)準(zhǔn)庫(kù)中妨托,SequenceType和CollectionType協(xié)議都有個(gè)叫l(wèi)azy的計(jì)算屬性缸榛,它能返回一個(gè)特殊的LazySequence或LazyCollection。
這些類型只能被用到map始鱼、filter仔掸、flatMap這樣的高階函數(shù)中,而且是以一種惰性的方式医清。
對(duì)于那些不需要完全運(yùn)行起暮,可能提前退出的情況,使用lazy來(lái)進(jìn)行性能優(yōu)化效果會(huì)非常有效会烙。
func increment(x: Int) -> Int {
print("訪問(wèn):\(x)")
return x + 1
}
let array = Array(0..<10)
print("直接使用map的結(jié)果")
let incrementArr = array.map(increment)
print(incrementArr[5])
print("\n使用lazy屬性的結(jié)果")
let lazyIncrementArr = array.lazy.map(increment)
print(lazyIncrementArr[5])
輸出的結(jié)果:
直接使用map的結(jié)果:
訪問(wèn):0
訪問(wèn):1
訪問(wèn):2
訪問(wèn):3
訪問(wèn):4
訪問(wèn):5
訪問(wèn):6
訪問(wèn):7
訪問(wèn):8
訪問(wèn):9
6
使用lazy屬性的結(jié)果:
訪問(wèn):5
6
- 直接使用map负懦,所有的輸出值都被計(jì)算出來(lái)了!即使只讀了第5個(gè)元素柏腻。
- 使用了lazy纸厉,僅調(diào)用了第5個(gè)元素的計(jì)算,其他元素計(jì)算并不會(huì)被調(diào)用五嫂。
使用lazy后颗品,計(jì)算量明顯降低很多。如果array的體量更大沃缘,且increment更復(fù)雜躯枢,那么節(jié)省的計(jì)算量就更明顯了。
懶加載情景
- 全局的常量/變量都是懶加載的槐臀,不需要標(biāo)記lazy
- 標(biāo)記為static的存儲(chǔ)型的類型屬性(常量變量)也是懶加載的锄蹂,不需要標(biāo)記lazy;(補(bǔ)充:static可以修飾存儲(chǔ)型的和計(jì)算型的類型屬性水慨,class可以修飾類類型的計(jì)算型的類型屬性)
- 延遲屬性:必須使用lazy var標(biāo)記得糜;(延遲屬性只能使用var修飾敬扛,不能使用let修飾)
延遲屬性與閉包
- 延遲屬性使用lazy var標(biāo)記聲明,其初始值可以使用直接方式創(chuàng)建朝抖,也可以使用閉包方式創(chuàng)建啥箭;
// 直接方式創(chuàng)建
lazy var person1: Person = Person()
// 閉包方式創(chuàng)建
lazy var person2: Person = {
let person = Person()
p.name = "Tom"
print("Tom...")
return person
}()
- 閉包不僅僅可以給延遲屬性(lazy var)設(shè)置初始值,也可以給全局的常/變量治宣、類的屬性捉蚤、對(duì)象的屬性進(jìn)行初始值,計(jì)算屬性本質(zhì)通過(guò)閉包實(shí)現(xiàn)的炼七;
let i = {
return 0
}()
class Person {
static let a = {
return 1
}()
var b = {
return 2
}()
}
- 在布局UI控件的時(shí)候缆巧,也可以使用閉包方式來(lái)完成初始化賦值,這樣更便于復(fù)用豌拙、更簡(jiǎn)潔陕悬;
let leftButton: UIButton = {
let button = UIButton(frame: buttonSize)
button.backgroundColor = .black
button.titleLabel?.text = "left"
return button
}()
let rightButton: UIButton = {
let button = UIButton(frame: buttonSize)
button.backgroundColor = .black
button.titleLabel?.text = "right"
return button
}()
- 延遲屬性使用閉包方式創(chuàng)建,在閉包中使用self不會(huì)產(chǎn)生循環(huán)引用按傅;全局的常/變量捉超、類的屬性、對(duì)象的屬性使用閉包方式創(chuàng)建唯绍,在閉包中無(wú)法使用self拼岳;