Sequences | lazy

序列 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拼岳;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市况芒,隨后出現(xiàn)的幾起案子惜纸,更是在濱河造成了極大的恐慌,老刑警劉巖绝骚,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耐版,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡压汪,警方通過(guò)查閱死者的電腦和手機(jī)粪牲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)止剖,“玉大人腺阳,你說(shuō)我怎么就攤上這事〈┫悖” “怎么了亭引?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)扔水。 經(jīng)常有香客問(wèn)我痛侍,道長(zhǎng)朝氓,這世上最難降的妖魔是什么魔市? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任主届,我火速辦了婚禮,結(jié)果婚禮上待德,老公的妹妹穿的比我還像新娘君丁。我一直安慰自己,他們只是感情好将宪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布绘闷。 她就那樣靜靜地躺著,像睡著了一般较坛。 火紅的嫁衣襯著肌膚如雪印蔗。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,954評(píng)論 1 283
  • 那天丑勤,我揣著相機(jī)與錄音华嘹,去河邊找鬼。 笑死法竞,一個(gè)胖子當(dāng)著我的面吹牛耙厚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播岔霸,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼薛躬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呆细?” 一聲冷哼從身側(cè)響起型宝,我...
    開(kāi)封第一講書(shū)人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎絮爷,沒(méi)想到半個(gè)月后诡曙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡略水,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年价卤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片渊涝。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慎璧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跨释,到底是詐尸還是另有隱情胸私,我是刑警寧澤,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布鳖谈,位于F島的核電站岁疼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缆娃。R本人自食惡果不足惜捷绒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一瑰排、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧暖侨,春花似錦椭住、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至葫掉,卻和暖如春些举,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俭厚。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工金拒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人套腹。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓绪抛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親电禀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子幢码,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容