Swift原型設(shè)計(jì)模式

啥叫原型模式?

創(chuàng)建一個(gè)新的對(duì)象,然后通過復(fù)制現(xiàn)有的對(duì)象,稱為原型。
原型本身使用模板創(chuàng)建,后續(xù)實(shí)例是克隆模板產(chǎn)生的。

問題 答案
這是什么? 通過復(fù)制現(xiàn)有的對(duì)象,稱為原型,原型模式創(chuàng)建新的對(duì)象。
有哪些好處? 主要的好處就是隱藏代碼,創(chuàng)建對(duì)象的組件,使用它們;這意味著組件不需要知道哪個(gè)類或創(chuàng)建一個(gè)新的對(duì)象所需的結(jié)構(gòu),不需要知道細(xì)節(jié)的初始值設(shè)定項(xiàng),而不需要更改當(dāng)子類創(chuàng)建和實(shí)例化。這種模式也可以用來(lái)避免重復(fù)昂貴初始化每次創(chuàng)建一個(gè)新的對(duì)象的特定類型。
什么時(shí)候用這種模式? 當(dāng)你正在編寫一個(gè)組件,需要?jiǎng)?chuàng)建對(duì)象的新實(shí)例,而無(wú)需在類初始值設(shè)定項(xiàng)創(chuàng)建依賴項(xiàng)時(shí),此模式非常有用。
何時(shí)應(yīng)該避免使用這種模式? 有使用此模式,沒有任何缺點(diǎn)。
如何知道你使用了這個(gè)模式? 若要測(cè)試此模式的有效實(shí)現(xiàn),請(qǐng)更改類或結(jié)構(gòu)用于原型對(duì)象并檢查是否作相應(yīng)的改變是需要?jiǎng)?chuàng)建克隆的組件中的初始值設(shè)定項(xiàng)。作為第二個(gè)測(cè)試創(chuàng)建原型的類的子類,并確保組件可以克隆它無(wú)需進(jìn)行任何更改。
是否有任何常見的陷阱? 最主要的陷阱選擇復(fù)制克隆的原型對(duì)象時(shí)的錯(cuò)誤樣式。有兩種的復(fù)制可用 — — 淺層和深層 — — 也是很重要,要選擇那種合適您的應(yīng)用程序。請(qǐng)參閱"理解淺層和深層復(fù)制"部分了解詳細(xì)信息。
有任何相關(guān)的模式嗎? 最密切相關(guān)的模式是描述對(duì)象模板模式。單例模式,提供一種手段,可以共享單個(gè)對(duì)象,避免需要?jiǎng)?chuàng)建其他實(shí)例。

理解模式所處理的問題

當(dāng)我們使用模板來(lái)創(chuàng)建對(duì)象,但這是一個(gè)辦法,有其自身的缺陷。

初始化消耗過大

一些類或結(jié)構(gòu)的模板消耗非常大,意思初始化對(duì)象的一個(gè)新實(shí)例可以消耗大量的內(nèi)存或計(jì)算為準(zhǔn)備使用的對(duì)象。為了證明這種問題,下面的代碼。

class Sum {
    var resultsCache: [[Int]]
    var firstValue:Int
    var secondValue:Int   
    init(first:Int, second:Int) {         
        resultsCache = [[Int]](count: 10, repeatedValue:[Int](count:10, repeatedValue: 0))
        for i in 0..<10 {             
            for j in 0..<10 {
                 self.resultsCache[i][j] = i + j;
            }
        }
        self.firstValue = first         
        self.secondValue = second
    }
     
    var Result:Int {         
        get {
            return firstValue < resultsCache.count && secondValue < resultsCache[firstValue].count ? 
resultsCache[firstValue][secondValue]:firstValue + secondValue
            //等價(jià)
            if firstValue < resultsCache.count && secondValue < resultsCache[firstValue].count {
                return resultsCache[firstValue][secondValue]
             }else{
                return firstValue + secondValue
            }
        }
    }
}  
var calc1 = Sum(first:0, second: 9).Result 
var calc2 = Sum(first:3, second: 8).Result
 
print("Calc1: \(calc1) Calc2: \(calc2)");
//9 , 12

我定義了一個(gè)名為類總和,產(chǎn)生傳遞到其初始值設(shè)定項(xiàng)的兩個(gè)整數(shù)值的總和。作為一種優(yōu)化的初始值設(shè)定項(xiàng)筆類創(chuàng)建一個(gè)二維 Int 數(shù)組,以交易時(shí)間在晚些時(shí)候?qū)斓倪\(yùn)算速度初始化過程中預(yù)先計(jì)算好的值填充。
有定義總和類,我然后創(chuàng)建兩個(gè)實(shí)例,并使用它們來(lái)執(zhí)行計(jì)算。我創(chuàng)建一個(gè)新的每次總和對(duì)象,我招致創(chuàng)建和填充二維陣列的成本 — — 可以被測(cè)量在存儲(chǔ)計(jì)算的值所需的內(nèi)存和計(jì)算成本。我完成的兩個(gè)運(yùn)算結(jié)果寫入控制臺(tái),產(chǎn)生下面的輸出 ︰


Calc1: 9 Calc2: 11


這可能看起來(lái)像一個(gè)不切實(shí)際的例子,但這種編碼風(fēng)格是很常見,通常是過早的優(yōu)化,其中一個(gè)程序員試圖推測(cè)提高代碼的性能,因?yàn)樗粚懀皇怯捎诤罄m(xù)的性能測(cè)試的結(jié)果 — — 的東西,通常會(huì)導(dǎo)致糟糕的性能和更少的可讀的代碼。然而,這也是在此示例中是不現(xiàn)實(shí)的兩個(gè)方面。第一個(gè)是工作由總和類是如此簡(jiǎn)單,即使是最狂熱的優(yōu)化器是不太可能看到添加兩個(gè)整數(shù)作為值得緩存的成本。第二個(gè)方面是playground上顯示總和類和創(chuàng)建實(shí)例從它在同一個(gè)文件的兩個(gè)語(yǔ)句。在實(shí)際項(xiàng)目中,初始化代碼迷失在深的層次結(jié)構(gòu)的類,并使用類的語(yǔ)句將在完全不同的應(yīng)用程序部分

創(chuàng)建模板的依賴關(guān)系
從模板創(chuàng)建一個(gè)新對(duì)象,組件必須具備三條信息。
■ 這是與對(duì)象關(guān)聯(lián)的模板
■ 初始值設(shè)定項(xiàng),必須調(diào)用
■ 名稱和類型初始值設(shè)定項(xiàng)參數(shù)
對(duì)象的一個(gè)新實(shí)例在哪里需要將此信息傳播整個(gè)應(yīng)用程序。這帶來(lái)的問題是它在模板上,創(chuàng)建一個(gè)依賴項(xiàng)這樣當(dāng)模板更改時(shí),所有使用該模板創(chuàng)建新對(duì)象的組件必須更新以反映該更改。你可以看到這在清單 5-2,在那里我已經(jīng)修改總和類,它定義了一個(gè)額外的初始值設(shè)定項(xiàng)的參數(shù)。

class Sum {
    var resultsCache: [[Int]]
    var firstValue:Int     
    var secondValue:Int
    init(first:Int, second:Int, cacheSize:Int) {         
        resultsCache = [[Int]](count: cacheSize, repeatedValue:[Int](count:cacheSize, repeatedValue: 0))         
        for i in 0 ..< cacheSize {             
            for j in 0 ..< cacheSize {                 
                resultsCache[i][j] = i + j
            }
        }
        self.firstValue = first         
        self.secondValue = second
    }      
    var Result:Int {         
        get {
            return firstValue < resultsCache.count
                && secondValue < resultsCache[firstValue].count
            ? resultsCache[firstValue][secondValue]
            : firstValue + secondValue
        }
    }
}  
var calc1 = Sum(first:0, second: 9, cacheSize:100).Result; 
var calc2 = Sum(first:3, second: 8, cacheSize:20).Result
 
println("Calc1: \(calc1) Calc2: \(calc2)");

初始值設(shè)定項(xiàng)參數(shù)用于控制生成的緩存結(jié)果的數(shù)目。如清單所示,我不得不更新創(chuàng)建語(yǔ)句總和對(duì)象使用訂正的初始值設(shè)定項(xiàng)。變更是平凡的當(dāng)只有兩個(gè)語(yǔ)句,創(chuàng)建總和對(duì)象是彼此相鄰,但這些建設(shè)語(yǔ)句可以分布在實(shí)際項(xiàng)目中,和每一個(gè)需有足夠的知識(shí)關(guān)于執(zhí)行總和類來(lái)提供一個(gè)合理的值為 cacheSize 初始值設(shè)定項(xiàng)參數(shù)。

提示我在這一章的目標(biāo)是展示原型模式,但還有其他的方法來(lái)解決這類問題。我可以例如,定義一個(gè)方便的初始值設(shè)定項(xiàng),調(diào)用指定的初始值設(shè)定項(xiàng)和提供的默認(rèn)值 cacheSize 參數(shù)。如第 1 章中,模式并不總是解決問題的唯一方法。

理解原型模式
原型模式使用一個(gè)現(xiàn)有的對(duì)象 — — 而不是類或結(jié)構(gòu) — — 以創(chuàng)建新的對(duì)象。這是通常指作為克隆,因?yàn)樾聦?duì)象是完全相同的副本,在現(xiàn)有的包括對(duì)已創(chuàng)建以來(lái),它的對(duì)象的存儲(chǔ)屬性所做的任何更改。圖 5-1 演示原型模式的工作原理。

有 3 個(gè)操作的原型模式。首先,需要對(duì)象的組件要求原始對(duì)象 (稱為原型) 進(jìn)行自我復(fù)制。第二次手術(shù)是在其中創(chuàng)建新的對(duì)象 (稱為克隆) 的復(fù)制過程。在最后一個(gè)操作,原型給調(diào)用組件的克隆,完成復(fù)制過程。

實(shí)現(xiàn)原型模式
Swift 自動(dòng)應(yīng)用原型模式,當(dāng)你到一個(gè)新的變量指派值類型。值類型定義使用結(jié)構(gòu),和所有類型的內(nèi)置swift實(shí)現(xiàn)為幕后,意味著你可以只是通過將他們分配給新變量克隆字符串、 布爾值、 集合、 枚舉、 元組和數(shù)值類型的結(jié)構(gòu)。swift將復(fù)制原型的價(jià)值,并使用它來(lái)創(chuàng)建一個(gè)克隆。清單 5-3 所示的內(nèi)容 ValueTypes.playground文件,我創(chuàng)建了演示如何克隆值類型.

struct Appointment {     
    var name:String     
    var day:String     
    var place:String
     
    func printDetails(label:String) {
        println("\(label) with \(name) on \(day) at \(place)");
    }
}  
    var beerMeeting = Appointment(name: "Bob", day: "Mon", place: "Joe's Bar")
 
    var workMeeting = beerMeeting
    workMeeting.name = "Alice"
    workMeeting.day = "Fri"
    workMeeting.place = "Conference Rm 2"
 
beerMeeting.printDetails("Social") 
workMeeting.printDetails("Work")

原型模式依賴于那里正在 — — 顯然不夠 — — 原型對(duì)象,通常從模板創(chuàng)建。這似乎有悖常理,但你必須要能夠以某種方式得到的原型。一旦我有原型,我將它分配給一個(gè)新的變量,這樣創(chuàng)建副本 ︰

...
var workMeeting = beerMeeting;
...

在這一點(diǎn)上,我有兩個(gè)單獨(dú)的任命分配給對(duì)象beerMeeting和workMeeting變量。天的名稱, ,和放置兩個(gè)對(duì)象的屬性具有相同的值,如圖所示的5 2.

圖 5-2。克隆一個(gè)結(jié)構(gòu)原型的影響
一旦我創(chuàng)建克隆,我分配到新值名稱, 一天,和地方配置克隆來(lái)代表不同的約會(huì)從一個(gè)由原型,如圖所示的屬性5-3.

圖 5-3。配置克隆
我完成通過調(diào)用printDetails方法對(duì)兩個(gè)任命對(duì)象,會(huì)產(chǎn)生以下結(jié)果 ︰

Social with Bob on Mon at Joe's Bar
Work with Alice on Fri at Conference Rm 2

一旦我創(chuàng)建的原型,我可以創(chuàng)建和配置盡可能多的克隆,因?yàn)槲倚枰粫?huì)招致與使用結(jié)構(gòu)模板相關(guān)的開銷。
克隆的引用類型
使用類創(chuàng)建的對(duì)象是引用類型,和 Swift 不會(huì)復(fù)制這些對(duì)象,當(dāng)你將它們分配給一個(gè)新的變量。相反,對(duì)對(duì)象的新引用創(chuàng)建這兩個(gè)變量引用同一個(gè)對(duì)象。清單 5-4 顯示的內(nèi)容 ReferenceTypes.playground 文件,在其中我已經(jīng)修改任命從前面的章節(jié),將類用作模板而不是一個(gè)結(jié)構(gòu)的例子。

class Appointment {
     var name:String; 
     var day:String; 
     var place:String;
     init(name:String, day:String, place:String) { 
          self.name = name; 
          self.day = day; 
          self.place = place;
    }
     func printDetails(label:String) { println("\(label) with \(name) on \(day) at \(place)");
    }
}
var beerMeeting = Appointment(name: "Bob", day: "Mon", place: "Joe's Bar");
var workMeeting = beerMeeting;
workMeeting.name = "Alice";
workMeeting.day = "Fri";
workMeeting.place = "Conference Rm 2";
beerMeeting.printDetails("Social");
workMeeting.printDetails("Work");

除了使用類關(guān)鍵字,我添加了一個(gè)初始值設(shè)定項(xiàng)對(duì)任命類。Swift 創(chuàng)建一個(gè)默認(rèn)初始值設(shè)定項(xiàng)為結(jié)構(gòu)而不是類。除了使用一個(gè)類來(lái)定義任命類型,本示例包含相同的代碼清單 5-3。安慰,然而,所示的結(jié)果不同。

上周五在會(huì)議 Rm 2 的 Alice 與社會(huì)
與愛麗絲于星期五工作會(huì)議 Rm 2

這里的問題就是其中一個(gè)任命對(duì)象,和它指由兩個(gè)workMeeting和beerMeeting變量,如圖所示

因?yàn)橹挥幸粋€(gè)任命對(duì)象、 發(fā)生變化,使到存儲(chǔ)的屬性通過workMeeting回來(lái)時(shí)我訪問通過屬性,讀取變量beerMeeting變量,這就是為什么我得到意想不到的事情 — — 并且無(wú)益 — — 在控制臺(tái)輸出。

執(zhí)行 NSCopying 議定書 》
分配新對(duì)象的引用現(xiàn)有是面向?qū)ο缶幊痰闹匾M成部分,但它是沒有幫助的原型模式。支持克隆, 基金會(huì)框架定義了 NSCopying 協(xié)議,該對(duì)話框允許您指定應(yīng)如何克隆的對(duì)象。清單 5-5 顯示了我如何更新任命類來(lái)實(shí)現(xiàn) NSCopying 協(xié)議。

import Foundation
    class Appointment : NSObject, NSCopying {
         var name:String;
         var day:String; 
         var place:String;
         init(name:String, day:String, place:String){ 
              self.name = name; 
              self.day = day; 
              self.place = place;
    }
        func printDetails(label:String) { 
              print("\(label) with \(name) on \(day) at \(place)");
    }
        func copyWithZone(zone: NSZone) -> AnyObject { 
              return Appointment(name:self.name, day:self.day, place:self.place);
    }

}

var beerMeeting = Appointment(name: "Bob", day: "Mon", place: "Joe's Bar");
var workMeeting = beerMeeting.copy() as Appointment;
workMeeting.name = "Alice";
workMeeting.day = "Fri";
workMeeting.place = "Conference Rm 2";
beerMeeting.printDetails("Social");
workMeeting.printDetails("Work");

提示可以實(shí)現(xiàn)NSCopying僅在類和非結(jié)構(gòu)的協(xié)議。結(jié)構(gòu)是總是主題
對(duì)淺層復(fù)制。

NSCopying協(xié)議定義了copyWithZone當(dāng)復(fù)制該對(duì)象時(shí)調(diào)用的方法。
用來(lái)復(fù)制對(duì)象的機(jī)制留給類來(lái)實(shí)現(xiàn),并在這種情況下,我創(chuàng)建的一個(gè)新實(shí)例任命類使用從當(dāng)前對(duì)象的存儲(chǔ)的屬性值。

你可以忽略NSZone在論證時(shí),執(zhí)行copyWithZone方法。

利用NSCopying協(xié)議,我要改變?nèi)蚊撬鼜呐缮愵怤SObject,它定義了副本方法。若要復(fù)制任命,我打電話給副本方法 — — 和不 copyWithZone— — 為原型,像這樣 ︰

...
var workMeeting = beerMeeting.copy() as Appointment;
...

copyWithZone方法返回AnyObject,這意味著我有向下轉(zhuǎn)換由創(chuàng)建的對(duì)象副本方法與作為關(guān)鍵字以便workMeeting變量鍵入正確。

警告執(zhí)行NSCopying協(xié)議不會(huì)更改為引用類型轉(zhuǎn)換為值類型。你必須調(diào)用副本克隆原型的方法。如果你只是將原型分配給一個(gè)新的變量,您將結(jié)束對(duì)原型并不是一個(gè)新的對(duì)象的新引用。

了解淺層和深層復(fù)制
原型模式的的一個(gè)重要方面是是否使用深復(fù)制或淺復(fù)制,它涉及如何存儲(chǔ)引用的屬性類型處理其他參考克隆對(duì)象。在清單 5-6,我定義了一類新的 ReferenceTypes playground和添加新屬性設(shè)置為約會(huì) ,是指它的實(shí)例的類

import Foundation
    class Location { 
        var name:String;
        var address:String;
        init(name:String, address:String) { 
            self.name = name; 
            self.address = address;
    }
}
    class Appointment : NSObject, NSCopying {  
        var name:String;
        var day:String;
        var place:Location;
        init(name:String, day:String, place:Location) {
             self.name = name; 
             self.day = day; 
             self.place = place; 
    }
       func printDetails(label:String) {
       print("\(label) with \(name) on \(day) at \(place.name), " + "\(place.address)");
}
       func copyWithZone(zone: NSZone) -> AnyObject { 
             return Appointment(name:self.name, day:self.day,place:self.place);
    }
}
var beerMeeting = Appointment(name: "Bob", day: "Mon", 
                  place: Location(name:"Joe's Bar", address: "123 Main St"))
var workMeeting = beerMeeting.copy() as Appointment;
workMeeting.name = "Alice";
workMeeting.day = "Fri";
workMeeting.place.name = "Conference Rm 2";
workMeeting.place.address = "Company HQ";
beerMeeting.printDetails("Social");
workMeeting.printDetails("Work");

我已經(jīng)創(chuàng)建了一個(gè)簡(jiǎn)單的位置類并使用它為地方屬性的任命對(duì)象。這里是顯示在控制臺(tái)中的輸出 ︰

Social with Bob on Mon at Conference Rm 2, Company HQ
Work with Alice on Fri at Conference Rm 2, Company HQ

再一次的變化通過所workMeeting變量有受影響的存儲(chǔ)的值可通過workMeeting變量。這是因?yàn)槲腋淖兊念愋头胖脤傩缘闹殿愋?(字符串) 為引用類型 (位置) 和我實(shí)現(xiàn)的 NSCopying 協(xié)議創(chuàng)建新引用的原型對(duì)象

這是稱為淺表副本,在其中復(fù)制對(duì)象的引用,不是對(duì)象本身。如圖 5-5 所示,有兩個(gè)約會(huì)對(duì)象,但其放置屬性引用同一個(gè)位置對(duì)象。這就是為什么我申請(qǐng)通過的變化 workMeeting.place 屬性會(huì)影響我通過的值 beerMeeting.place 屬性。

提示與NSCopying協(xié)議是@NSCopying屬性特性,可以應(yīng)用于存儲(chǔ)屬性。 @NSCopying 屬性將自動(dòng)調(diào)用 copyWithZone 方法的對(duì)象指派給的附加說(shuō)明的屬性,和我將演示其使用在本章后面的"示例的原型模式在可可粉"一節(jié)。

實(shí)施深度復(fù)制
深度復(fù)制每個(gè)創(chuàng)建的原型,而在這種情況下將確保所提到的所有對(duì)象的副本任命對(duì)象是指不同位置對(duì)象通過其地方屬性。清單 5-7 顯示了如何實(shí)現(xiàn)深度復(fù)制在 ReferenceTypes playground。

import Foundation
class Location : NSObject, NSCopying {
         var name:String; 
         var address:String;
         init(name:String, address:String) { 
                self.name = name; 
                self.address = address;
    }
        func copyWithZone(zone: NSZone) -> AnyObject { 
                return Location(name: self.name, address:self.address);
    }
}

class Appointment : NSObject, NSCopying { 
    var name:String;
    var day:String; 
    var place:Location;
    init(name:String, day:String, place:Location) { 
        self.name = name; 
        self.day = day; 
        self.place = place;
    }
    func printDetails(label:String) { 
        print("\(label) with \(name) on \(day) at \(place.name), "+ "\(place.address)");
    }
    func copyWithZone(zone: NSZone) -> AnyObject { 
        return Appointment(name:self.name, day:self.day,place:self.place.copy() as Location);
    }
}
var beerMeeting = Appointment(name: "Bob", day: "Mon", 
                               place: Location(name:"Joe's Bar", address: "123 Main St"));
var workMeeting = beerMeeting.copy() as Appointment;

workMeeting.name = "Alice";
workMeeting.day = "Fri";
workMeeting.place.name = "Conference Rm 2";
workMeeting.place.address = "Company HQ";
beerMeeting.printDetails("Social");
workMeeting.printDetails("Work");

若要?jiǎng)?chuàng)建深層副本,必須執(zhí)行NSCopying議定書 》位置類,改變?cè)摶怤SObject,并定義copyWithZone方法。你要深拷貝的所有引用類型必須都實(shí)現(xiàn) NSCopying 你的原型,包括那些通過其他提及所提到的協(xié)議,所以您必須重復(fù)此過程在整個(gè)類引用。

選擇淺或深復(fù)制
有沒有什么一成不變的規(guī)則之間淺層和深層復(fù)制,選擇和決定,便在類的類的基礎(chǔ)上作出。你應(yīng)該考慮三個(gè)因素 ︰ 所需復(fù)制的對(duì)象的存儲(chǔ)復(fù)制和復(fù)制的對(duì)象將被使用的方式所需的內(nèi)存量的工作量。
它是最后一個(gè)因素 — — 將如何使用復(fù)制的對(duì)象 — — 那就是最重要。情況下預(yù)約對(duì)象毫無(wú)任何意義,因?yàn)閷?duì)某個(gè)約會(huì)的位置所做的更改不太可能將應(yīng)用于其他約會(huì),引用同一個(gè)位置的位置在清單 5-7,之間共享對(duì)象類對(duì)象,尤其是當(dāng)社會(huì)和工作任用混合在一起。
它很可能是一組相關(guān)的約會(huì),將受益于一個(gè)共享位置。想象一下,作為一個(gè)例子,一整天都在同一會(huì)議室舉行的會(huì)議的系列 — — 您的應(yīng)用程序可能會(huì)受益于優(yōu)化創(chuàng)造的位置對(duì)象的相關(guān)會(huì)議。在這種情況下,您應(yīng)該平衡計(jì)算和創(chuàng)建并保存一個(gè)新的對(duì)象,針對(duì)管理對(duì)共享對(duì)象的引用的復(fù)雜性所需內(nèi)存的量。在任命/位置示例中, 位置對(duì)象很容易創(chuàng)建和需要這種很少的存儲(chǔ) (只是兩個(gè)字符串值) 的開銷和復(fù)雜性的工作什么時(shí)候位置可以共享對(duì)象和當(dāng)他們不能只是不合理的。
我能給的最好建議是想通過對(duì)象和圖的目的出哪些擬共同跨越所有副本創(chuàng)建從原型。如果你不確定,然后開始與淺復(fù)制,因?yàn)樗亲詈?jiǎn)單的執(zhí)行 — — 它不會(huì)總是正確的技術(shù),但它允許您測(cè)試變動(dòng)的影響,而不必執(zhí)行 NSCopying 在您的應(yīng)用程序協(xié)議。

由于前面提到的實(shí)施NSCopying協(xié)議不會(huì)改變引用類型轉(zhuǎn)換為值類型,所以我必須調(diào)用副本方法來(lái)創(chuàng)建一個(gè)克隆任命原型的位置對(duì)象,我在做copyWithZone所定義的方法任命班上

func copyWithZone(zone: NSZone) -> AnyObject {
      return Appointment(name:self.name, day:self.day,place:self.place.copy() as Location);
}

通過查看控制臺(tái)輸出從這里所示的示例代碼,您可以看到深拷貝的影響 ︰

Social with Bob on Mon at Joe's Bar, 123 Main St
Work with Alice on Fri at Conference Rm 2, Company HQ

由于任命對(duì)象有自己位置對(duì)象、 發(fā)生變化,可通過workMeeting變量有沒有通過訪問對(duì)象上的效果beerMeeting變量。

復(fù)制陣列
swift數(shù)組是作為結(jié)構(gòu),這使得它們值類型實(shí)現(xiàn)的。當(dāng)你將一個(gè)數(shù)組分配給一個(gè)新的變量時(shí),該數(shù)組本身被復(fù)制以及它包含任何值類型。數(shù)組中包含的引用類型是淺層復(fù)制,這樣原型數(shù)組和克隆數(shù)組都將包含對(duì)同一對(duì)象的引用。清單 5-8 所示的內(nèi)容 ArrayCopy.playground 文件,造成提供示范。

import Foundation
class Person : NSObject, NSCopying { 
    var name:String;
    var country:String;
    init(name:String, country:String) { 
        self.name = name; 
        self.country = country;
  }
    func copyWithZone(zone: NSZone) -> AnyObject { 
        return Person(name: self.name, country: self.country);
  }
}
var people = [Person(name:"Joe", country:"France"), Person(name:"Bob", country:"USA")];
var otherpeople = people;
people[0].country = "UK";
print("Country: \(otherpeople[0].country)");

提示作為一種性能優(yōu)化,swift陣列不復(fù)制直到你修改它們,稱為懶的復(fù)制。這不是你需要擔(dān)心在日常的基礎(chǔ)上,因?yàn)樗鼰o(wú)縫地發(fā)生在幕后,但它意味著如果你只在讀該數(shù)組的內(nèi)容,像一次值類型引用類型進(jìn)行修改像克隆的數(shù)組。

我創(chuàng)建的數(shù)組稱為人包含兩個(gè)人對(duì)象。我將數(shù)組賦給一個(gè)變量稱為別人 ,然后再修改中的第一個(gè)對(duì)象人數(shù)組。這里是控制臺(tái)的輸出,顯示出該數(shù)組的內(nèi)容確實(shí)淺復(fù)制,即使陣列本身是一個(gè)結(jié)構(gòu) ︰

Country: UK

深深地復(fù)制數(shù)組,必須檢查每個(gè)數(shù)組中的項(xiàng)和查找的對(duì)象的類從派生NSObject和實(shí)現(xiàn)NSCopying協(xié)議,如清單 5-9 所示。

import Foundation
class Person : NSObject, NSCopying { 
    var name:String;
    var country:String;
    init(name:String, country:String) { 
        self.name = name; 
        self.country = country;
  }
   func copyWithZone(zone: NSZone) -> AnyObject { 
        return Person(name: self.name, country: self.country);
  }
}
   func deepCopy(data:[AnyObject]) -> [AnyObject] { 
        return data.map({item -> AnyObject in
         if (item is NSCopying && item is NSObject) { 
            return (item as NSObject).copy();
         } else { 
            return item;
         }
    })
}
var people = [Person(name:"Joe", country:"France"), Person(name:"Bob", country:"USA")];
var otherpeople = deepCopy(people) as [Person];
people[0].country = "UK";
print("Country: \(otherpeople[0].country)");

提示在"示例的原型模式在可可粉"部分中,我解釋如何復(fù)制可可陣列,由執(zhí)行NSArray和NSMutableArray類。這些類以不同的方式對(duì)待我在本節(jié)中描述的內(nèi)置swift陣列和了解他們的工作可以是有用的使用目標(biāo) C 代碼時(shí)。

我定義了一個(gè)稱為函數(shù)deepCopy接受一個(gè)數(shù)組并使用地圖方法將復(fù)制數(shù)組。我遞給關(guān)閉地圖方法檢查是否該對(duì)象可以是 deepcopied,如果它可以調(diào)用副本方法。其他對(duì)象添加到結(jié)果數(shù)組而無(wú)需修改。此控制臺(tái)輸出所示,深深地復(fù)制的陣列不再包含對(duì)同一對(duì)象的引用 ︰

Country: France

了解原型模式的好處
在以下章節(jié)中,我描述了原型模式提供的好處。其中一些解決章開頭有關(guān)的問題卻從對(duì)象復(fù)制通過的方式產(chǎn)生的額外好處 NSCopying 協(xié)議。

避免昂貴的初始化
使用NSCopying協(xié)議允許對(duì)象采取負(fù)責(zé)復(fù)制自己,這意味著克隆可以避免昂貴的初始化操作。在這一章的開始,我用 Initialization.playground 文件來(lái)定義總和在其初始值設(shè)定項(xiàng),我在其中已重復(fù)清單 5-10 生成緩存結(jié)果的數(shù)組類。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評(píng)論 1 287
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評(píng)論 2 374

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