Cocoa设计模式(iOS常用设计模式) Cocoa Design Patterns
Cocoa設(shè)計(jì)模式
Cocoa環(huán)境的許多架構(gòu)和機(jī)制都能夠有效地使用設(shè)計(jì)模式:抽象設(shè)計(jì)可以解決特定環(huán)境中的重復(fù)問(wèn)題。 本文描述了Cocoa中設(shè)計(jì)模式的主要實(shí)現(xiàn),主要關(guān)注模型(Model) - 視圖(View) - 控制器(Controller)和對(duì)象建模。 本章的主要目的是讓您更深入地了解Cocoa的設(shè)計(jì)模式,并鼓勵(lì)您在自己的軟件項(xiàng)目中利用這些模式。
什么是設(shè)計(jì)模式?
設(shè)計(jì)模式是設(shè)計(jì)的模板,它可以在特定的上下文中解決一般的、重復(fù)出現(xiàn)的問(wèn)題。它是一種抽象工具,在建筑、工程和軟件開(kāi)發(fā)等領(lǐng)域都很有用。下面的部分總結(jié)了設(shè)計(jì)模式是什么,解釋了為什么它們對(duì)于面向?qū)ο笤O(shè)計(jì)很重要,并討論了一個(gè)示例設(shè)計(jì)模式。
一個(gè)問(wèn)題的解決方案
作為開(kāi)發(fā)人員,您可能已經(jīng)熟悉面向?qū)ο缶幊讨械脑O(shè)計(jì)模式概念。 他們首先由Erich Gamma,Richard Helm,Ralph Johnson和John Vlissides(通常被稱(chēng)為“四人幫”)設(shè)計(jì)模式:可重用面向?qū)ο筌浖脑剡M(jìn)行了權(quán)威性的描述和編目。 這本書(shū)最初發(fā)表于1994年,不久之后還有其他書(shū)籍和文章,進(jìn)一步探討和闡述了面向?qū)ο笙到y(tǒng)中的設(shè)計(jì)模式。
設(shè)計(jì)模式的簡(jiǎn)潔定義是“對(duì)上下文中的問(wèn)題的解決方案”。讓我們通過(guò)反向解釋這個(gè)短語(yǔ)來(lái)解析這個(gè)問(wèn)題。 上下文是該模式適用的反復(fù)出現(xiàn)的情況。 問(wèn)題是你在這個(gè)上下文中試圖達(dá)到的目標(biāo),以及上下文所帶來(lái)的任何約束。 解決方案就是你所追求的目標(biāo):通用的上下文設(shè)計(jì),達(dá)到目標(biāo)并解決約束。
設(shè)計(jì)模式將具體設(shè)計(jì)結(jié)構(gòu)的關(guān)鍵方面抽象出來(lái),這已經(jīng)被證明是有效的。 該模式有一個(gè)名稱(chēng),并標(biāo)識(shí)參與該模式的類(lèi)和對(duì)象及其職責(zé)和協(xié)作。 它還列出了后果(成本和收益)以及該模式可以應(yīng)用的情況。 設(shè)計(jì)模式是特定設(shè)計(jì)的一種模板或指南; 從某種意義上講,具體的設(shè)計(jì)就是一種模式的“實(shí)例化”。 設(shè)計(jì)模式并不是絕對(duì)的。 在如何應(yīng)用它們方面有一定的靈活性,通常諸如編程語(yǔ)言和現(xiàn)有體系結(jié)構(gòu)的東西可以決定如何應(yīng)用模式。
設(shè)計(jì)的幾個(gè)主題或原則影響設(shè)計(jì)模式。 這些 設(shè)計(jì)原則是構(gòu)建面向?qū)ο笙到y(tǒng)的經(jīng)驗(yàn)法則,如“封裝不同系統(tǒng)結(jié)構(gòu)的各個(gè)方面”,“編程是為了接口而不是實(shí)現(xiàn)”,它們表達(dá)了重要的見(jiàn)解。 例如,如果您隔離系統(tǒng)中各部分并將其封裝起來(lái),那么它們可以獨(dú)立于系統(tǒng)的其他部分而變化,特別是如果您為它們定義了與實(shí)現(xiàn)細(xì)節(jié)無(wú)關(guān)的接口。 您稍后可以更改或擴(kuò)展這些可變部分,而不會(huì)影響系統(tǒng)的其他部分。 因此,您可以消除依賴(lài)性并減少部件之間的耦合,從而使系統(tǒng)變得更加靈活和易于更改。
在編寫(xiě)軟件時(shí),設(shè)計(jì)模式是一個(gè)重要的考慮因素。 如果您在程序設(shè)計(jì)中找到,適應(yīng)和使用模式,那么程序(包括它所包含的對(duì)象和類(lèi))將更具可重用性,可擴(kuò)展性和更容易隨著未來(lái)需求的變化而改變。 而且,基于設(shè)計(jì)模式的程序通常比不考慮設(shè)計(jì)模式的程序更加優(yōu)雅和高效,因?yàn)樗鼈冎恍枰俚拇a就能實(shí)現(xiàn)相同的目標(biāo)。
舉例:命令模式(Command Pattern)
“四人幫”的大部分書(shū)籍都是由一個(gè)設(shè)計(jì)模式目錄組成的。 它按范圍(類(lèi)或?qū)ο?#xff09;和目的(創(chuàng)建,結(jié)構(gòu)或行為)對(duì)目錄中的模式進(jìn)行分類(lèi)。 目錄中的每個(gè)條目討論設(shè)計(jì)模式的意圖,動(dòng)機(jī),適用性,結(jié)構(gòu),參與者,合作,后果和實(shí)現(xiàn)。 其中一個(gè)條目是 命令模式(一種對(duì)象行為模式)。
本書(shū)陳述Command模式的意圖是“將請(qǐng)求封裝為一個(gè)對(duì)象,從而允許您使用不同的請(qǐng)求參數(shù)化客戶(hù)端,排隊(duì)或記錄請(qǐng)求,并支持可撤銷(xiāo)操作”。模式將發(fā)送消息的對(duì)象與接收和評(píng)估這些消息的對(duì)象分隔開(kāi)。 消息的發(fā)起者(客戶(hù)端)通過(guò)將特定接收者上的一個(gè)或多個(gè)動(dòng)作綁定在一起來(lái)封裝請(qǐng)求。 封裝的消息可以在對(duì)象之間傳遞,放置在隊(duì)列中或以其他方式存儲(chǔ)以用于稍后的調(diào)用,并且動(dòng)態(tài)地修改以改變接收器或消息參數(shù)。 下圖顯示了該模式的結(jié)構(gòu)圖。
對(duì)于一個(gè)熟悉Cocoa的開(kāi)發(fā)者來(lái)說(shuō),命令模式的這個(gè)簡(jiǎn)短的概述可能會(huì)敲響一個(gè)鐘聲。 該模式完美地描述了Foundation框架中的一個(gè)類(lèi),其目的是封裝消息: NSInvocation。 正如該模式的意圖狀態(tài),其目的之一是使操作可撤銷(xiāo)。 調(diào)用對(duì)象用于Cocoa設(shè)計(jì)中 撤銷(xiāo)管理以及分布式對(duì)象,這是一個(gè)進(jìn)程間通信的體系結(jié)構(gòu)。 命令模式也描述(雖然不太完美)Cocoa的target-action機(jī)制,其中用戶(hù)界面控制對(duì)象封裝了用戶(hù)激活時(shí)發(fā)送的消息的目標(biāo)和動(dòng)作。
在其框架類(lèi),語(yǔ)言和runtime,Cocoa已經(jīng)為您實(shí)現(xiàn)了很多編目設(shè)計(jì)模式。 (這些實(shí)現(xiàn)在下面有描述。)通過(guò)使用設(shè)計(jì)模式的“現(xiàn)成”適應(yīng)性,可以滿(mǎn)足您的許多開(kāi)發(fā)需求。 或者你可以根據(jù)你的問(wèn)題,它的上下文來(lái)需要一個(gè)你自己的全新的基于模式的設(shè)計(jì)。 重要的是在開(kāi)發(fā)軟件時(shí)要有意識(shí)的注意模式,并在適當(dāng)?shù)臅r(shí)候在設(shè)計(jì)中使用它們。
Cocoa如何改變?cè)O(shè)計(jì)模式
可以在OS X和iOS版本中找到適用于Cocoa的設(shè)計(jì)模式。 基于模式的機(jī)制和體系結(jié)構(gòu)在Cocoa框架和Objective-C 的 runtime 和語(yǔ)言中很常見(jiàn)。 Cocoa經(jīng)常把自己獨(dú)特的旋律放在一個(gè)模式上,因?yàn)樗脑O(shè)計(jì)受語(yǔ)言能力或現(xiàn)有體系結(jié)構(gòu)等因素的影響。
本節(jié)包含大多數(shù)設(shè)計(jì)模式的摘要,這些設(shè)計(jì)模式是在“ 設(shè)計(jì)模式:可重用面向?qū)ο筌浖脑亍敝芯幠康摹?每個(gè)部分不僅總結(jié)了模式,還討論了Cocoa的實(shí)現(xiàn)。 只列出Cocoa實(shí)現(xiàn)的模式,以下各節(jié)中的模式描述都與特定的Cocoa上下文有關(guān)。
Cocoa設(shè)計(jì)模式的實(shí)現(xiàn)有多種形式。 以下部分中描述的一些設(shè)計(jì)(如協(xié)議和類(lèi)別)是 Objective-C語(yǔ)言的特性。 在一些情況下,“模式實(shí)例”是在一個(gè)類(lèi)或一組相關(guān)類(lèi)(例如,類(lèi)集群和單例類(lèi))中實(shí)現(xiàn)的。 在另一些情況下,模式適應(yīng)是一個(gè)主要的框架體系結(jié)構(gòu),例如響應(yīng)者鏈。 一些基于模式的機(jī)制幾乎可以“免費(fèi)”獲得,而另外一些機(jī)制則需要做一些工作。 即使Cocoa沒(méi)有實(shí)現(xiàn)一個(gè)模式,當(dāng)情況需要時(shí),你自己可以這樣實(shí)現(xiàn); 例如,對(duì)象組合(Decorator模式)通常比繼承類(lèi)的行為更好。
兩個(gè)設(shè)計(jì)模式保留給后面的部分,模型 - 視圖 - 控制器(MVC)和對(duì)象建模。 MVC是一種復(fù)合或聚合模式,這意味著它基于幾種目錄模式。 對(duì)象建模在“四人幫”目錄中沒(méi)有對(duì)應(yīng),而是源于關(guān)系數(shù)據(jù)庫(kù)的領(lǐng)域。 然而MVC和對(duì)象建模也許是Cocoa中最重要,最普遍的設(shè)計(jì)模式,在很大程度上它們是相互關(guān)聯(lián)的模式。 它們?cè)诎ń壎?#xff0c;撤銷(xiāo)管理,腳本和文檔體系結(jié)構(gòu)在內(nèi)的多種技術(shù)的設(shè)計(jì)中起著至關(guān)重要的作用。
抽象工廠(Abstract Factory)
抽象工廠模式提供了一個(gè)接口,用于創(chuàng)建相關(guān)或依賴(lài)對(duì)象的族,而不指定具體的類(lèi)。 客戶(hù)與從工廠獲得的具體對(duì)象的任何具體細(xì)節(jié)分離。
類(lèi)集群
類(lèi)集群是一種將公共抽象父類(lèi)下的許多私有具體子類(lèi)組織在一起的體系結(jié)構(gòu)。 抽象父類(lèi)聲明了創(chuàng)建其私有子類(lèi)實(shí)例的方法。 父類(lèi)根據(jù)調(diào)用的創(chuàng)建方法分配適當(dāng)?shù)木唧w子類(lèi)的對(duì)象。 每個(gè)返回的對(duì)象可能屬于不同的私有具體子類(lèi)。
Cocoa中的類(lèi)集群只能生成其數(shù)據(jù)存儲(chǔ)可能因環(huán)境而異的對(duì)象。 Foundation框架具有NSString ,NSData, NSDictionary,NSSet和NSArray對(duì)象的類(lèi)集群。 公共父類(lèi)包括這些不可變類(lèi)以及互補(bǔ)的可變類(lèi)NSMutableString , NSMutableData ,NSMutableDictionary , NSMutableSet和NSMutableArray。
使用和限制
如果要?jiǎng)?chuàng)建集群表示類(lèi)型的不可變或可變對(duì)象,則可以使用類(lèi)集群的公共類(lèi)之一。 對(duì)于類(lèi)集群,在簡(jiǎn)單性和可擴(kuò)展性之間進(jìn)行權(quán)衡。 一個(gè)類(lèi)集群簡(jiǎn)化了一個(gè)類(lèi)的接口,從而更容易學(xué)習(xí)和使用這個(gè)類(lèi)。 但是,創(chuàng)建類(lèi)集群的抽象父類(lèi)的自定義子類(lèi)通常比較困難。
適配器
適配器設(shè)計(jì)模式將類(lèi)的接口轉(zhuǎn)換為客戶(hù)期望的另一個(gè)接口。 適配器讓類(lèi)能夠協(xié)同工作。 它將客戶(hù)從目標(biāo)對(duì)象的類(lèi)中分離出來(lái)。
協(xié)議 (Protocols)
一個(gè) 協(xié)議是一種語(yǔ)言級(jí)(Objective-C)功能,可以定義適配器模式實(shí)例的接口。 協(xié)議本質(zhì)上是一系列與類(lèi)無(wú)關(guān)的方法聲明。 (在Java中, 接口與協(xié)議是同義的)。如果你想要一個(gè)客戶(hù)端對(duì)象與另一個(gè)對(duì)象進(jìn)行通信,但是對(duì)象的不兼容接口使得這個(gè)接口變得困難,你可以定義一個(gè)協(xié)議。 另一個(gè)對(duì)象的類(lèi)然后正式采用該協(xié)議,并通過(guò)實(shí)現(xiàn)該協(xié)議的一個(gè)或多個(gè)方法來(lái)“符合”該協(xié)議。 該協(xié)議可能要求符合類(lèi)實(shí)現(xiàn)其一些方法,并可能使其他的實(shí)現(xiàn)可選。 然后客戶(hù)端對(duì)象可以通過(guò)協(xié)議接口將消息發(fā)送到另一個(gè)對(duì)象。
協(xié)議創(chuàng)建了一組獨(dú)立于類(lèi)層次結(jié)構(gòu)的方法聲明。 它們可以在協(xié)議一致性和類(lèi)繼承的基礎(chǔ)上對(duì)對(duì)象進(jìn)行分組。 NSObject方法 conformsToProtocol:允許您驗(yàn)證對(duì)象的協(xié)議從屬關(guān)系。
Cocoa有非正式協(xié)議以及正式協(xié)議。 非正式協(xié)議是NSObject類(lèi)中的一個(gè)類(lèi)別,因此可以使任何對(duì)象成為該類(lèi)別中任何方法的潛在實(shí)現(xiàn)者。 非正式協(xié)議中的方法可以有選擇地實(shí)現(xiàn)。 非正式協(xié)議是OS X中委托機(jī)制實(shí)施的一部分。
請(qǐng)注意,協(xié)議的設(shè)計(jì)并不完全匹配適配器模式的描述。 但是它實(shí)現(xiàn)了這個(gè)模式的目標(biāo):允許具有不兼容接口的類(lèi)一起工作。
使用和限制
您主要使用一個(gè)協(xié)議來(lái)聲明一個(gè)接口,如果他們想要進(jìn)行通信,那么層次上無(wú)關(guān)的類(lèi)將被預(yù)期符合。 但是你也可以使用協(xié)議來(lái)聲明一個(gè)對(duì)象的接口,同時(shí)隱藏它的類(lèi)。 Cocoa框架包括許多正式的協(xié)議,使定制的子類(lèi)能夠與特定的目的進(jìn)行通信。 例如,Foundation框架包含NSObject,NSCopying和NSCoding協(xié)議,這些協(xié)議都非常重要。 AppKit協(xié)議包括 NSDraggingInfo , NSTextInput ,和 NSChangeSpelling。 UIKit協(xié)議包括 UITextInputTraits , UIWebViewDelegate ,和 UITableViewDataSource 。
正式的協(xié)議隱式地要求符合類(lèi)來(lái)實(shí)現(xiàn)所有聲明的方法。 但是,他們可以用@optional指令標(biāo)記單個(gè)方法或方法組,并且符合類(lèi)可以選擇執(zhí)行這些方法。 他們也很脆弱; 一旦你定義了一個(gè)協(xié)議并使其可用于其他類(lèi),將來(lái)對(duì)其進(jìn)行的更改(除了附加的可選方法外)可能會(huì)破壞這些類(lèi)。
責(zé)任鏈
責(zé)任鏈設(shè)計(jì)模式通過(guò)給予多個(gè)對(duì)象一個(gè)機(jī)會(huì)來(lái)處理請(qǐng)求,從而將請(qǐng)求的發(fā)送者與其接收者分離。 該模式將接收對(duì)象鏈接在一起,并沿著鏈傳遞請(qǐng)求,直到對(duì)象處理它為止。 鏈中的每個(gè)對(duì)象處理請(qǐng)求或?qū)⑵鋫鬟f給鏈中的下一個(gè)對(duì)象。
響應(yīng)者鏈
應(yīng)用程序框架包括稱(chēng)為響應(yīng)者鏈的體系結(jié)構(gòu)。 這個(gè)鏈由一系列響應(yīng)者對(duì)象組成(也就是繼承對(duì)象) NSResponder 或者,在UIKit中, UIResponder ),一個(gè)事件(例如,一個(gè)鼠標(biāo)點(diǎn)擊)或動(dòng)作消息被傳遞(通常)最終被處理。 如果給定的響應(yīng)者對(duì)象不處理特定的消息,則將消息傳遞給鏈中的下一個(gè)響應(yīng)者。 響應(yīng)者對(duì)象在鏈中的順序通常由視圖層次結(jié)構(gòu)決定,從層次結(jié)構(gòu)中的低級(jí)響應(yīng)者到高層響應(yīng)者的進(jìn)程,最終在管理視圖層次的窗口對(duì)象,窗口對(duì)象的委托,或全局應(yīng)用程序?qū)ο蟆?響應(yīng)者鏈上的事件和行動(dòng)消息的路徑是不同的。 應(yīng)用程序可以擁有盡可能多的響應(yīng)者鏈,因?yàn)樗哂写翱?#xff08;甚至是本地層次的視圖)。 但是一次只能有一個(gè)響應(yīng)者鏈,即與當(dāng)前活動(dòng)窗口關(guān)聯(lián)的響應(yīng)者鏈。
AppKit框架對(duì)錯(cuò)誤處理也實(shí)現(xiàn)了類(lèi)似的響應(yīng)鏈。
iOS注意: UIKit實(shí)現(xiàn)的響應(yīng)者鏈不同于AppKit。 如果一個(gè)視圖是由一個(gè) UIViewController 對(duì)象,視圖控制器成為鏈中的下一個(gè)響應(yīng)者(從那里事件或動(dòng)作消息傳遞到視圖的超級(jí)視圖)。 另外,UIKit本身不支持文檔體系結(jié)構(gòu); 因此響應(yīng)者鏈中沒(méi)有文檔對(duì)象或窗口控制器對(duì)象。 iOS中也沒(méi)有錯(cuò)誤處理應(yīng)答鏈。
視圖層次結(jié)構(gòu)與響應(yīng)者鏈緊密相關(guān),調(diào)整復(fù)合模式的設(shè)計(jì) 。 動(dòng)作消息(來(lái)自控制對(duì)象的消息)基于target-action機(jī)制,它是命令模式的一個(gè)實(shí)例。
使用和限制
當(dāng)您使用Interface Builder或以編程方式為程序構(gòu)建用戶(hù)界面時(shí),您可以免費(fèi)獲得一個(gè)或多個(gè)響應(yīng)者鏈。響應(yīng)者鏈與視圖層次結(jié)構(gòu)緊密相關(guān),您在創(chuàng)建視圖時(shí)會(huì)自動(dòng)獲取對(duì)象窗口的內(nèi)容視圖的子視圖。 如果您將自定義視圖添加到視圖層次結(jié)構(gòu)中,它將成為響應(yīng)者鏈的一部分。 如果您實(shí)施適當(dāng)?shù)腘SResponder或UIResponder方法,則可以接收和處理事件和操作消息。 作為窗口對(duì)象或全局應(yīng)用程序?qū)ο蟮奈械淖远x對(duì)象(AppKit中的NSApp )也可以接收和處理這些消息。
您也可以通過(guò)編程方式將自定義響應(yīng)者注入到響應(yīng)者鏈中,并且可以通過(guò)編程方式操縱響應(yīng)者的順序。
命令
命令設(shè)計(jì)模式將請(qǐng)求作為對(duì)象進(jìn)行封裝,從而使您可以使用不同請(qǐng)求參數(shù)化客戶(hù)端,排隊(duì)或記錄請(qǐng)求,并支持可撤銷(xiāo)操作。 請(qǐng)求對(duì)象將特定接收者上的一個(gè)或多個(gè)動(dòng)作綁定在一起。 命令模式將發(fā)出請(qǐng)求的對(duì)象與接收和執(zhí)行該請(qǐng)求的對(duì)象分開(kāi)。
調(diào)用對(duì)象
一個(gè)實(shí)例 NSInvocation 類(lèi)封裝了一個(gè) Objective-C消息。 一個(gè)調(diào)用對(duì)象包含一個(gè)目標(biāo)對(duì)象,方法選擇器和方法參數(shù)。 您可以動(dòng)態(tài)地更改調(diào)用對(duì)象調(diào)度的消息的目標(biāo)及其參數(shù); 一旦調(diào)用被執(zhí)行,您也可以從對(duì)象中獲取返回值。 使用單個(gè)調(diào)用對(duì)象,您可以重復(fù)調(diào)用目標(biāo)和參數(shù)具有多種變化的消息。
創(chuàng)建一個(gè)NSInvocation對(duì)象需要一個(gè)NSMethodSignature對(duì)象,它是一個(gè)封裝了與方法的參數(shù)和返回值有關(guān)的類(lèi)型信息的對(duì)象。 NSMethodSignature對(duì)象又是從方法選擇器創(chuàng)建的。 NSInvocation的實(shí)現(xiàn)也利用了Objective-C runtime 的功能。
使用和限制
NSInvocation對(duì)象是分布式對(duì)象的編程接口,撤銷(xiāo)管理,消息轉(zhuǎn)發(fā)和定時(shí)器的一部分。 您還可以在類(lèi)似的上下文中使用調(diào)用對(duì)象,您需要從接收消息的對(duì)象中分離發(fā)送消息的對(duì)象。
分布式對(duì)象技術(shù)用于進(jìn)程間通信。
Target-Action 機(jī)制
該 target-action機(jī)制使控件對(duì)象(即按鈕,滑塊或文本字段等對(duì)象)能夠?qū)⑾l(fā)送到另一個(gè)可以解釋消息并將其作為特定于應(yīng)用程序的指令處理的對(duì)象。 接收對(duì)象,或者 目標(biāo),通常是一個(gè)自定義控制器對(duì)象。 消息命名為 動(dòng)作消息 - 由選擇器確定,一個(gè)方法的唯一運(yùn)行時(shí)標(biāo)識(shí)符。
在AppKit框架中,控件擁有的單元對(duì)象通常封裝了目標(biāo)和動(dòng)作。 當(dāng)用戶(hù)點(diǎn)擊或以其他方式激活控件時(shí),控件從其單元中提取信息并發(fā)送消息。 (菜單項(xiàng)還封裝了目標(biāo)和動(dòng)作,并在用戶(hù)選擇時(shí)發(fā)送一個(gè)動(dòng)作消息。)目標(biāo)動(dòng)作機(jī)制可以基于選擇器(而不是方法簽名)工作,因?yàn)閯?dòng)作方法的簽名在按照慣例AppKit總是相同的。
在UIKit中,目標(biāo)操作機(jī)制不依賴(lài)于單元格。 相反,控件會(huì)將目標(biāo)和操作映射到控件上可能發(fā)生的一個(gè)或多個(gè)多點(diǎn)觸控事件。
使用和限制
創(chuàng)建Cocoa應(yīng)用程序時(shí),可以通過(guò)Interface Builder應(yīng)用程序設(shè)置控件的操作和目標(biāo)。 因此,您可以讓控件啟動(dòng)自定義行為,而無(wú)需為控件本身編寫(xiě)任何代碼。 動(dòng)作選擇器和目標(biāo)連接存檔在nib文件中,并在nib被取消存檔時(shí)恢復(fù)。 您也可以通過(guò)發(fā)送控件或其單元格setTarget:和setAction: messages來(lái)動(dòng)態(tài)更改目標(biāo)和動(dòng)作。
用于OS X的Cocoa應(yīng)用程序可以使用目標(biāo)操作機(jī)制來(lái)指示自定義控制器對(duì)象將數(shù)據(jù)從用戶(hù)界面?zhèn)鬏數(shù)侥P蛯?duì)象,或者在模型對(duì)象中顯示數(shù)據(jù)。 Cocoa綁定技術(shù)避免了為此目的使用目標(biāo)動(dòng)作的需要。 看到 可可綁定編程主題 關(guān)于這項(xiàng)技術(shù)的更多信息。
Controls和cell不保留其目標(biāo)。
復(fù)合
復(fù)合設(shè)計(jì)模式將相關(guān)對(duì)象組合成樹(shù)結(jié)構(gòu)來(lái)表示部分 - 整體層次結(jié)構(gòu)。 該模式可以讓客戶(hù)統(tǒng)一處理個(gè)別對(duì)象和對(duì)象組合。
Composite模式是Model-View-Controller聚合模式的一部分
查看層次結(jié)構(gòu)
View ( NSView 要么 UIView 對(duì)象)在一個(gè)窗口內(nèi)部結(jié)構(gòu)化成一個(gè) 視圖層次。 在層次結(jié)構(gòu)的根部是一個(gè)窗口( NSWindow 要么 UIWindow 對(duì)象)及其內(nèi)容視圖,一個(gè)透明視圖,填充窗口的內(nèi)容矩形。 添加到內(nèi)容視圖中的視圖成為其子視圖,并成為添加到視圖中的所有視圖的超級(jí)視圖。 除了內(nèi)容視圖外,視圖還有一個(gè)(也是唯一的) superview和零或任何數(shù)量的 子視圖。 你認(rèn)為這個(gè)結(jié)構(gòu)是遏制:父視圖包含它的子視圖。 圖4-2顯示了視圖層次結(jié)構(gòu)的視覺(jué)和結(jié)構(gòu)方面。
視圖層次結(jié)構(gòu)是在繪圖和事件處理中起作用的結(jié)構(gòu)體系結(jié)構(gòu)。 一個(gè)視圖有兩個(gè)邊界矩形,它的框架和邊界,這些矩形會(huì)影響視圖的圖形操作。 框架是外部邊界; 它將視圖定位在其超級(jí)視圖的坐標(biāo)系中,定義其大小,并將繪圖剪輯到視圖的邊緣。 邊界(內(nèi)部邊界矩形)定義視圖自身繪制的曲面的內(nèi)部坐標(biāo)系。
當(dāng)窗口系統(tǒng)要求窗口準(zhǔn)備顯示時(shí),要求超級(jí)視圖在其子視圖之前進(jìn)行渲染。 當(dāng)你發(fā)送一些消息到一個(gè)視圖 - 例如,一個(gè)請(qǐng)求視圖重繪自己的消息 - 消息傳播到子視圖。 因此,您可以將視圖層次結(jié)構(gòu)的分支視為統(tǒng)一的視圖。
響應(yīng)者鏈也使用視圖層次來(lái)處理事件和動(dòng)作消息。
使用和限制
無(wú)論是以編程方式還是使用Interface Builder將視圖添加到其他視圖,都可以創(chuàng)建或修改視圖層次結(jié)構(gòu)。 AppKit框架自動(dòng)處理與視圖層次關(guān)聯(lián)的所有關(guān)系。
裝飾器
裝飾設(shè)計(jì)模式動(dòng)態(tài)地將額外的責(zé)任附加到對(duì)象上。 裝飾器為擴(kuò)展功能提供了子類(lèi)化的靈活替代方案。 與子類(lèi)化一樣,修飾器模式的適應(yīng)性允許您在不修改現(xiàn)有代碼的情況下合并新的行為。 裝飾器包裝了它們擴(kuò)展行為的類(lèi)的一個(gè)對(duì)象。 它們實(shí)現(xiàn)與它們所包裝的對(duì)象相同的接口,并在將任務(wù)委托給包裝對(duì)象之前或之后添加它們自己的行為。 Decorator模式表達(dá)了這樣的設(shè)計(jì)原則:類(lèi)應(yīng)該是可以拓展的,而不需要修改類(lèi)。
常規(guī)注釋
裝飾者是一個(gè)對(duì)象組合模式,非常鼓勵(lì)這樣做。 但是,Cocoa提供了一些自己的類(lèi)和機(jī)制(在下面的部分討論)是基于裝飾模式。 在這些實(shí)現(xiàn)中,擴(kuò)展對(duì)象并不完全復(fù)制它所包裝的對(duì)象的接口,并且這些實(shí)現(xiàn)使用不同的技術(shù)來(lái)進(jìn)行接口共享。
Cocoa在其幾個(gè)類(lèi)的實(shí)現(xiàn)中使用了Decorator模式,包括NSAttributedString ,NSScrollView ,和 UIDatePicker 。 后兩個(gè)類(lèi)是復(fù)合視圖的例子,它將其他視圖類(lèi)的簡(jiǎn)單對(duì)象組合在一起,并協(xié)調(diào)它們的交互。
代理 (Delegation)
代理是一種機(jī)制,通過(guò)這種機(jī)制,宿主對(duì)象將一個(gè)弱引用(意味著它是一個(gè)簡(jiǎn)單的指針引用,而不是 retained)嵌入到另一個(gè)對(duì)象中,在需要輸入任務(wù)時(shí)定期向代理發(fā)送消息。 宿主對(duì)象通常是一個(gè)“現(xiàn)成的”框架對(duì)象(比如 NSWindow 要么 NSXMLParser 對(duì)象)正在尋求完成一些事情,但只能以一種通用的方式來(lái)實(shí)現(xiàn)。 代理幾乎總是一個(gè)自定義類(lèi)的實(shí)例,與主機(jī)對(duì)象協(xié)調(diào)工作,在任務(wù)的某些點(diǎn)提供程序特定的行為( 見(jiàn)下圖 )。 因此,委派可以修改或擴(kuò)展另一個(gè)對(duì)象的行為,而無(wú)需進(jìn)行子類(lèi)化。
代理,就一個(gè)對(duì)象委托給另一個(gè)對(duì)象的簡(jiǎn)單意義而言,是面向?qū)ο缶幊讨械某S眉夹g(shù)。 然而,Cocoa以一種獨(dú)特的方式實(shí)現(xiàn)了代理。 一個(gè)主機(jī)類(lèi)使用一個(gè) 正式協(xié)議或一個(gè) 非正式協(xié)議來(lái)定義委托對(duì)象可以選擇實(shí)現(xiàn)的接口。 非正式協(xié)議中的所有方法都是可選的,正式的協(xié)議可以聲明可選的方法,允許委托只實(shí)現(xiàn)協(xié)議中的一些方法。 在嘗試向其委托發(fā)送消息之前,主機(jī)對(duì)象決定是否實(shí)現(xiàn)該方法(通過(guò)respondsToSelector:消息)以避免運(yùn)行時(shí)異常。
Cocoa框架中的一些類(lèi)也向它們的數(shù)據(jù)源發(fā)送消息。 數(shù)據(jù)源在所有方面都與代理相同,除了意圖是向主機(jī)對(duì)象提供數(shù)據(jù)以填充瀏覽器,表視圖或類(lèi)似的用戶(hù)界面視圖。 與委托人不同,數(shù)據(jù)源也可能需要實(shí)現(xiàn)協(xié)議的某些方法。
代理不是Decorator模式的嚴(yán)格實(shí)現(xiàn)。 主機(jī)(delegating)對(duì)象不包裝它想要擴(kuò)展的類(lèi)的一個(gè)實(shí)例; 事實(shí)上,這是另一回事,委托人正在專(zhuān)門(mén)委托框架類(lèi)的行為。 除了由框架類(lèi)聲明的代理方法,也沒(méi)有接口的共享。
Cocoa的代理也是模板方法模式的一部分。
使用和限制
代理是Cocoa框架中的一個(gè)常見(jiàn)設(shè)計(jì)。 AppKit和UIKit框架中的許多類(lèi)將消息發(fā)送給代理,包括 NSApplication , UIApplication , UITableView ,和幾個(gè)子類(lèi) NSView 。 Foundation框架中的一些類(lèi),例如NSXMLParser和 NSStream ,也維護(hù)代理。 除非委托方法不允許您實(shí)現(xiàn)您的目標(biāo),否則應(yīng)始終使用類(lèi)的代理機(jī)制,而不是繼承類(lèi)的子類(lèi)。
盡管可以動(dòng)態(tài)更改代理,但一次只能有一個(gè)對(duì)象成為代理。 因此,如果您希望多個(gè)對(duì)象同時(shí)通知特定的程序事件,則不能使用代理。 但是,您可以為此使用通知機(jī)制。 只要委托實(shí)現(xiàn)框架類(lèi)聲明的一個(gè)或多個(gè)通知方法,代理就會(huì)自動(dòng)從其委托框架對(duì)象接收通知。
在AppKit中代理對(duì)象不保留代理或數(shù)據(jù)源。
分類(lèi)(Categories)
一個(gè) category是Objective-C語(yǔ)言的一個(gè)特性,它使您能夠?qū)⒎椒?#xff08;接口和實(shí)現(xiàn))添加到類(lèi)中,而無(wú)需創(chuàng)建子類(lèi)。 在程序范圍內(nèi),類(lèi)的原始方法和類(lèi)別添加的方法之間沒(méi)有運(yùn)行時(shí)差異。 類(lèi)別中的方法成為類(lèi)類(lèi)型的一部分,并由所有類(lèi)的子類(lèi)繼承。
和代理一樣,類(lèi)別并不是對(duì)裝飾者模式的嚴(yán)格適應(yīng),實(shí)現(xiàn)了意圖,而是采取了不同的途徑來(lái)實(shí)現(xiàn)這個(gè)意圖。 按類(lèi)別添加的行為是編譯時(shí)工件,并不是動(dòng)態(tài)獲取的。 而且,類(lèi)別不會(huì)封裝正在擴(kuò)展的類(lèi)的一個(gè)實(shí)例。
使用和限制
Cocoa框架定義了許多類(lèi)別,其中大多數(shù)是非正式的協(xié)議。 他們經(jīng)常使用類(lèi)別來(lái)分組相關(guān)的方法。 您可以在您的代碼中實(shí)現(xiàn)類(lèi)別來(lái)擴(kuò)展不帶子類(lèi)的類(lèi)或?qū)⑾嚓P(guān)方法分組。 但是,你應(yīng)該知道這些警告:
- 不能將實(shí)例變量添加到類(lèi)中。
- 如果您覆蓋了該類(lèi)的現(xiàn)有方法,那么您的應(yīng)用程序可能會(huì)表現(xiàn)出不可預(yù)測(cè)性。
外觀(Facade)
外觀設(shè)計(jì)模式為子系統(tǒng)中的一組接口提供統(tǒng)一的接口。 該模式定義了一個(gè)更高層次的接口,通過(guò)降低復(fù)雜度并隱藏子系統(tǒng)之間的通信和依賴(lài)關(guān)系,使子系統(tǒng)更易于使用。
NSImage
NSImage 類(lèi) ,AppKit框架提供一個(gè)統(tǒng)一的接口用于裝載和使用圖像,可以是基于位圖(例如在JPEG,PNG,TIFF或格式)或基于向量的(例如在EPS或PDF格式)。NSImage可以保持相同的圖像的一個(gè)以上表示;每個(gè)表示是一種 NSImageRep 對(duì)象。 NSImage自動(dòng)化選擇一種表現(xiàn),對(duì)于一個(gè)特定類(lèi)型的數(shù)據(jù)和對(duì)于給定的顯示裝置是合適的。它也隱藏了圖像操作和選擇的細(xì)節(jié),使客戶(hù)可以互換使用許多不同的底層表示。
用途和限制
由于NSImage支持的圖像就是幾個(gè)不同的表現(xiàn),一些請(qǐng)求的屬性可能不適用。例如,以像素的顏色請(qǐng)求圖像,如果底層圖像表示是基于矢量和設(shè)備無(wú)關(guān),則不起作用。
迭代器
Iterator設(shè)計(jì)模式提供了一種方法來(lái)順序訪問(wèn)聚合對(duì)象的(即,集合)元件,而不暴露其底層表示。迭代器模式轉(zhuǎn)換用于訪問(wèn)和遍歷從集合本身集合的元素添加到迭代器對(duì)象的責(zé)任。迭代器定義了一個(gè)接口,用于訪問(wèn)集合元素和跟蹤當(dāng)前的元素。不同的迭代器可以執(zhí)行不同的遍歷策略。
枚舉器
Foundation框架的NSEnumerator類(lèi)實(shí)現(xiàn)了迭代器模式。抽象NSEnumerator類(lèi)的私人、具體的子類(lèi)返回枚舉對(duì)象,順序返回集合中的對(duì)象給客戶(hù),有各種類(lèi)型-數(shù)組,集合,詞典(值和鍵)。
NSDirectoryEnumerator是遠(yuǎn)親類(lèi)。這個(gè)類(lèi)的實(shí)例遞歸枚舉在文件系統(tǒng)中目錄的內(nèi)容。
用途和限制
集合類(lèi)如NSArray,NSSet,和NSDictionary包括返回適當(dāng)集合類(lèi)型的枚舉方法。所有枚舉器以相同的方式工作。發(fā)送nextObject消息,當(dāng)退出循環(huán)時(shí),枚舉器對(duì)象nil返回,而不是集合中的下一個(gè)對(duì)象。
您還可以使用快速列舉訪問(wèn)集合中的元素; 這種語(yǔ)言功能是快速枚舉。
中介者模式(Mediator)
中介者模式定義了如何封裝一組互動(dòng)對(duì)象的對(duì)象。中介者促進(jìn)由稱(chēng)呼對(duì)方明確地保持對(duì)象的松散耦合,并可以獨(dú)立地改變它們之間的相互作用。因此,這些對(duì)象可以保持更多的重用。
此模式的一個(gè)“中介對(duì)象”集中在一個(gè)系統(tǒng)中對(duì)象之間復(fù)雜的通信和控制邏輯。這些對(duì)象告訴中介對(duì)象當(dāng)其狀態(tài)的變化,反過(guò)來(lái),從介體對(duì)象請(qǐng)求作出響應(yīng)。
AppKit框架控制器類(lèi)
模型 - 視圖 - 控制器設(shè)計(jì)模式給在一個(gè)面向?qū)ο蟮南到y(tǒng)(例如應(yīng)用)中的對(duì)象分配角色。它們可以是模型對(duì)象,其中包含應(yīng)用程序的數(shù)據(jù)和操作這些數(shù)據(jù); 它們可以是view對(duì)象,可以顯示數(shù)據(jù)和響應(yīng)用戶(hù)的動(dòng)作; 或者它們可以是控制器對(duì)象,作為所述模型和視圖對(duì)象之間的中介。控制器對(duì)象適合中介者模式。
在Cocoa中,控制器對(duì)象可以是兩種通用的類(lèi)型: 中介控制器或 協(xié)調(diào)控制器。中介調(diào)解的控制器查看對(duì)象和模型對(duì)象之間的數(shù)據(jù)在應(yīng)用程序的流程。中介控制器通常是NSController對(duì)象。協(xié)調(diào)控制器執(zhí)行集中通信和控制邏輯用于應(yīng)用,作為代理對(duì)框架對(duì)象和作為行動(dòng)的消息的目標(biāo)。他們是典型的NSWindowController對(duì)象或自定義NSObject對(duì)象子類(lèi)。因?yàn)樗麄兪侨绱烁叨葘?zhuān)業(yè)化的特定程序,以協(xié)調(diào)控制器往往不被重用。
抽象類(lèi)NSController和AppKit框架中它的具體子類(lèi)是Cocoa綁定技術(shù)的一部分,它會(huì)自動(dòng)同步包含在模型對(duì)象和顯示和查看對(duì)象編輯的數(shù)據(jù)的一部分。例如,如果用戶(hù)在text field編輯字符串,綁定技術(shù)傳達(dá)變化,通過(guò)一個(gè)中介控制器到綁定模型對(duì)象的相應(yīng)屬性。所有的程序員需要做的就是正確設(shè)計(jì)自己的模型對(duì)象,并使用Interface Builder,建立一個(gè)程序的視圖,控制器,和模型對(duì)象之間的綁定。
Interface Builder庫(kù)中具體的公共控制器類(lèi)的實(shí)例是可用的,因此是高度可重用。他們提供的服務(wù),如選擇和占位符值的管理。這些對(duì)象執(zhí)行以下具體功能:
- NSObjectController 管理一個(gè)模型對(duì)象。
- NSArrayController管理模型對(duì)象的數(shù)組,并保持一個(gè)選擇; 它也可以讓你的對(duì)象從array中添加和刪除對(duì)象。
- NSTreeController 使您能夠以分層樹(shù)結(jié)構(gòu)中添加,刪除和管理模型對(duì)象。
- NSUserDefaultsController提供了一個(gè)方便的接口首選項(xiàng)(用戶(hù)缺省值)系統(tǒng)。
用途和限制
通常使用NSController的對(duì)象作為中介控制器,因?yàn)檫@些對(duì)象是為了應(yīng)用程序查看對(duì)象和模型對(duì)象之間的通信數(shù)據(jù)來(lái)設(shè)計(jì)的。要使用中介控制器,您通常從Interface Builder的庫(kù)拖動(dòng)對(duì)象,指定模型對(duì)象的屬性鍵,并建立使用Interface Builder的信息窗口的綁定窗格視圖和模型對(duì)象之間的綁定。您也可以繼承NSController或它的一個(gè)子類(lèi)來(lái)獲得更專(zhuān)業(yè)的行為。
可以在任何一對(duì)對(duì)象之間都建立綁定,只要這些對(duì)象遵守NSKeyValueCoding和NSKeyValueObserving非正式協(xié)議。但為了NSController的優(yōu)勢(shì),最好是通過(guò)中介控制器進(jìn)行綁定。
協(xié)調(diào)控制器集中應(yīng)用程序的通信和控制邏輯:
- 維護(hù)outlets模型和視圖對(duì)象(outlets是保持連接或引用其他對(duì)象的實(shí)例變量)
- 通過(guò)目標(biāo)-動(dòng)作響應(yīng)view對(duì)象的用戶(hù)操縱
- 作為一個(gè)代理由框架對(duì)象發(fā)送的消息
通常做上述所有的連接,outlets,target-action,delegates,在Interface Builder,他們存檔在應(yīng)用程序的nib文件。
View Controllers in UIKit
iOS中運(yùn)行的應(yīng)用程序經(jīng)常使用一個(gè)模型和導(dǎo)航用戶(hù)界面的設(shè)計(jì),用于呈現(xiàn)屏幕上的應(yīng)用數(shù)據(jù)模型。一個(gè)應(yīng)用程序可以具有導(dǎo)航欄和工具欄,這些對(duì)象之間的是應(yīng)用數(shù)據(jù)的當(dāng)前視圖。用戶(hù)可以點(diǎn)擊工具欄上的按鈕來(lái)選擇一個(gè)模式,導(dǎo)航欄上的點(diǎn)擊按鈕,并且在當(dāng)前視圖點(diǎn)擊控制遍歷模型(數(shù)據(jù))對(duì)象的層次結(jié)構(gòu);每個(gè)級(jí)別上的中央視圖顯示更多的細(xì)節(jié)。在這個(gè)層次的結(jié)尾往往是用戶(hù)可以檢查或編輯的項(xiàng)目。
從UIViewController繼承的視圖控制器。UIViewController是一個(gè)抽象類(lèi),可以繼承管理特定視圖。UIKit框架還提供用于管理導(dǎo)航欄和工具欄對(duì)象子類(lèi): UINavigationController 和 UITabBarController。如下圖所示,一個(gè)tab-bar控制器可以管理許多導(dǎo)航控制器,這反過(guò)來(lái)又可以管理一個(gè)或多個(gè)視圖控制器,每個(gè)具有其相關(guān)聯(lián)的view對(duì)象的步驟。除了管理視圖(包括重疊視圖),一個(gè)視圖控制器指定了顯示在導(dǎo)航欄中的按鈕和標(biāo)題。
備忘錄模式(Memento)
備忘錄模式捕捉和外部化對(duì)象的內(nèi)部狀態(tài),而不違反封裝-使得對(duì)象可以恢復(fù)到這種狀態(tài)后。備忘錄模式保持關(guān)鍵對(duì)象外部的重要狀態(tài)。
歸檔
將對(duì)象存檔在一個(gè)程序中,隨著這些對(duì)象的屬性(屬性和關(guān)系)到檔案,可以存儲(chǔ)在文件系統(tǒng)或過(guò)程或在網(wǎng)絡(luò)之間傳輸。以字節(jié)存檔了一個(gè)程序的對(duì)象圖,保留對(duì)象的身份和他們之間的關(guān)系的一種體系結(jié)構(gòu)無(wú)關(guān)的流。因?yàn)橐粋€(gè)對(duì)象的類(lèi)型及其數(shù)據(jù)存儲(chǔ)對(duì)象解碼字節(jié)流通常實(shí)例化的對(duì)象使用原來(lái)的編碼相同的類(lèi)。
使用和限制
一般來(lái)說(shuō),你想保存一些程序中想要保存狀態(tài)的對(duì)象。模型對(duì)象幾乎都屬于這一類(lèi)。寫(xiě)一個(gè)對(duì)象的檔案進(jìn)行編碼和解碼,通過(guò)解碼從歸檔中讀取那個(gè)對(duì)象。編碼和解碼是你使用NSCoder對(duì)象執(zhí)行的操作,最好使用密鑰歸檔技術(shù)(你要調(diào)用的方法NSKeyedArchiver和NSKeyedUnarchiver類(lèi))。被編碼和解碼的對(duì)象必須符合NSCoding協(xié)議;該協(xié)議的方法被調(diào)用時(shí)歸檔
屬性列表序列化
屬性列表是一個(gè)簡(jiǎn)單的,使用下面類(lèi)對(duì)象結(jié)構(gòu)序列化的對(duì)象圖:NSDictionary, NSArray, NSString, NSData,NSDate, NSNumber.這些對(duì)象通常被稱(chēng)為屬性列表對(duì)象。幾個(gè)Cocoa框架類(lèi)提供的方法來(lái)將這些屬性列表對(duì)象和定義數(shù)據(jù)流記錄的對(duì)象及其層次關(guān)系的特殊格式的內(nèi)容。這個(gè)NSPropertyListSerialization類(lèi)提供的類(lèi)方法,可以從XML格式或優(yōu)化的二進(jìn)制格式,序列化屬性列表對(duì)象。
使用和限制
如果在對(duì)象圖中的對(duì)象是簡(jiǎn)單的,屬性列表序列化是一個(gè)靈活的,便攜的,和足夠的手段來(lái)捕捉和呈現(xiàn)對(duì)象及其狀態(tài)。然而,這種形式的序列化有其局限性。它不保存對(duì)象完整的標(biāo)識(shí),只是一般的類(lèi)型(數(shù)組,字典,字符串,等等)。因此,一個(gè)對(duì)象從屬性列表恢復(fù)可能是一個(gè)與原來(lái)類(lèi)不同的類(lèi)。這是一個(gè)問(wèn)題,當(dāng)一個(gè)對(duì)象的可變性可以不同。屬性列表序列化也不跟蹤,對(duì)象中多次引用的對(duì)象,可能會(huì)導(dǎo)致多個(gè)實(shí)例在反序列化,在原來(lái)的對(duì)象圖的一個(gè)實(shí)例。
Core Data
Core Data是Cocoa框架,定義了管理對(duì)象圖,使他們持續(xù)的架構(gòu)。正是這第二能力對(duì)象持久化使Core Data適應(yīng)備忘錄模式。
核心數(shù)據(jù)的設(shè)計(jì)也嚴(yán)重的影響了模型-視圖-控制器和對(duì)象建模模式。
使用和限制
Core Data在企業(yè)應(yīng)用的發(fā)開(kāi)中非常有用,模型對(duì)象的復(fù)雜圖必須定義,管理,和透明的歸檔和未歸檔和數(shù)據(jù)存儲(chǔ)。
核心數(shù)據(jù)是特別有用的企業(yè)應(yīng)用中的模型對(duì)象的復(fù)雜圖必須定義,管理,和透明的歸檔和未歸檔和數(shù)據(jù)存儲(chǔ)。Xcode開(kāi)發(fā)環(huán)境包括項(xiàng)目模板和設(shè)計(jì)工具,來(lái)減少需要?jiǎng)?chuàng)建核心數(shù)據(jù)應(yīng)用的兩種類(lèi)型的編程工作,那些基于文檔和那些不是基于文檔的數(shù)據(jù)。Interface Builder應(yīng)用還包括可配置的庫(kù)中的核心數(shù)據(jù)結(jié)構(gòu)對(duì)象。
觀察者(Observer)
觀察者設(shè)計(jì)模式定義了對(duì)象之間的一對(duì)多的依賴(lài)關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴(lài)于它的通知并自動(dòng)更新。觀察者模式本質(zhì)上是一個(gè)發(fā)布和訂閱模型,它的主體及其觀察員是松散耦合。觀察和觀察對(duì)象之間不需要知道對(duì)方就可以發(fā)生通信。
通知(Notifications)
Cocoa通知機(jī)制實(shí)現(xiàn)了一個(gè)基于觀察者模式的消息一對(duì)多廣播。在一個(gè)程序中的對(duì)象添加自己或其他對(duì)象的列表,有一個(gè)或多個(gè)通知觀察者,其中每一個(gè)是由全局的字符串識(shí)別(通知的名字)。要通知其它對(duì)象,觀察對(duì)象創(chuàng)建一個(gè)通知對(duì)象和推送到通知中心的對(duì)象。通知中心決定一個(gè)特定的通知觀察者,通過(guò)消息將通知發(fā)送到他們。該方法通過(guò)通知消息調(diào)用必須符合一定的單參數(shù)簽名。該方法的參數(shù)是通知對(duì)象,其中包含通知名,觀察對(duì)象,和一個(gè)包含任何補(bǔ)充信息字典。
推送通知是一個(gè)同步程序。發(fā)布對(duì)象不重新控制到通知中心有廣播通知所有的觀察者。對(duì)于異步行為,你可以在通知隊(duì)列中通知;控制立即返回到發(fā)布對(duì)象,當(dāng)通知到達(dá)隊(duì)列頂部時(shí)通知中心再?gòu)V播。
定期通知,這些通過(guò)通知中心的廣播只是進(jìn)程內(nèi)。如果你想給其他進(jìn)程廣播通知,您可以使用分布式通知中心及其相關(guān)的API。
使用和限制
你可以因?yàn)楦鞣N各樣的原因使用通知,。例如,可以廣播通知改變用戶(hù)界面元素的顯示信息,基于在程序的其他地方的某一事件。或者你可以用通知的方式確保目標(biāo)文檔中的文檔窗口關(guān)閉前保存它們的狀態(tài)。通知的目的是告知其他對(duì)象程序事件使他們能夠作出適當(dāng)?shù)姆磻?yīng)。
但對(duì)象接收通知的反應(yīng)只有在已經(jīng)發(fā)生的事件。這是和代理的顯著差異。代理有機(jī)會(huì)拒絕或修改由委托對(duì)象提出的操作。另一方面,觀察對(duì)象不能直接影響即將進(jìn)行的操作。
通知類(lèi)都是NSNotification(通知對(duì)象),NSNotificationCenter(推送通知和添加觀察員),NSNotificationQueue(隊(duì)列化通知),和NSDistributedNotificationCenter。許多Cocoa框架類(lèi)發(fā)布通知后,任何對(duì)象都可以觀察。
Key-Value Observing
KVO是一種機(jī)制,允許當(dāng)其他對(duì)象的具體特性的變化時(shí)對(duì)象被通知。它是基于NSKeyValueObserving 非正式協(xié)議。觀察到的屬性可以是簡(jiǎn)單的屬性,一個(gè)一關(guān)系,或一對(duì)多的關(guān)系。在模型視圖控制器模式下,鍵值觀察尤為重要,因?yàn)樗挂晥D對(duì)象模型對(duì)象的觀察通過(guò)控制器層的變化。因此,這是Cocoa的一個(gè)必不可少的綁定技術(shù)的組成部分。
Cocoa提供了一個(gè)默認(rèn)“自動(dòng)”的實(shí)施NSKeyValueObserving方法:給所有符合對(duì)象的屬性的觀察能力。
使用和限制
KVO是類(lèi)似的通知機(jī)制,但在一些重要的方面不同。在鍵值觀察沒(méi)有中央的對(duì)象,提供所有觀察員變更通知。相反,變化通知直接發(fā)送到觀察對(duì)象。鍵值觀察也直接關(guān)系到具體對(duì)象的屬性值。另一方面,通知機(jī)制,更廣泛的關(guān)注程序事件。
對(duì)象參與鍵值觀察(KVO)必須KVO兼容即遵守一定的要求。對(duì)于自動(dòng)觀測(cè),這需要符合要求的關(guān)鍵值編碼(KVC依從性)和使用KVC的依從性的方法(即,訪問(wèn)器方法)。鍵值編碼是一種機(jī)制(基于相關(guān)的非正式協(xié)議)自動(dòng)獲取和設(shè)置對(duì)象屬性的值。
你可以通過(guò)禁用自動(dòng)化KVO通知觀察者通知,使用方法NSKeyValueObserving非正式的協(xié)議和相關(guān)的類(lèi)執(zhí)行手動(dòng)通知。
代理(Proxy)
代理設(shè)計(jì)模式提供了一種替代,或占位符,為另一個(gè)對(duì)象來(lái)控制訪問(wèn)其他對(duì)象。使用這個(gè)模式來(lái)創(chuàng)建一個(gè)代表或代理,對(duì)象,控制訪問(wèn)另一個(gè)對(duì)象,這可能是遠(yuǎn)程的,復(fù)雜的創(chuàng)建,或需要保護(hù)。這種模式的結(jié)構(gòu)類(lèi)似于裝飾模式卻有不同的用途;裝飾給對(duì)象添加行為,而代理控制對(duì)對(duì)象的訪問(wèn)。
NSProxy
NSProxy類(lèi)定義了對(duì)象作為其他對(duì)象的代理對(duì)象的接口,甚至對(duì)象還不存在。一個(gè)代理對(duì)象通常轉(zhuǎn)發(fā)消息給它代表的對(duì)象,但它也可以響應(yīng)消息,通過(guò)加載對(duì)象轉(zhuǎn)變?yōu)樗虼怼km然NSProxy是一個(gè)抽象類(lèi),它實(shí)現(xiàn)了NSObject協(xié)議和其他基本方法的根對(duì)象的預(yù)期;事實(shí)上,一個(gè)層次結(jié)構(gòu)的基類(lèi),如NSObject類(lèi)
具體的NSProxy子類(lèi)能夠完成既定目標(biāo)的代理模式,如昂貴的對(duì)象或作為安全哨兵懶實(shí)例化對(duì)象。NSDistantObject,基礎(chǔ)框架中一個(gè)NSProxy具體子類(lèi),實(shí)現(xiàn)了一個(gè)遠(yuǎn)程代理,對(duì)于透明的分布式消息。NSDistantObject對(duì)象是分布式對(duì)象結(jié)構(gòu)的一部分。作為對(duì)其他進(jìn)程或線程對(duì)象的代理,它們有助于使這些線程或進(jìn)程的對(duì)象之間的通信。
NSInvocation 對(duì)象,這是命令模式的一種適應(yīng)體,也是分布式對(duì)象體系結(jié)構(gòu)的一部分
使用和限制
Cocoa只有在分布式對(duì)象采用NSProxy對(duì)象。這個(gè)NSProxy對(duì)象是具體的實(shí)例,是具體子類(lèi)NSDistantObject和NSProtocolChecker。不僅可以使用分布式對(duì)象為進(jìn)程消息(在相同的或不同的計(jì)算機(jī))也可以用它來(lái)實(shí)現(xiàn)分布式計(jì)算和并行處理。如果你想使用代理對(duì)象的其他用途,如昂貴的資源或安全的創(chuàng)造,你要實(shí)現(xiàn)你自己的具體的NSProxy子類(lèi)
接待員(Receptionist)
前臺(tái)設(shè)計(jì)模式是一種混合模式。雖然它沒(méi)有出現(xiàn)在“四人幫”的書(shū),它結(jié)合了命令,備忘錄的元素,和本文中描述的代理設(shè)計(jì)模式。這也是蹦床模式(Trampoline pattern)的一個(gè)變種(其中還沒(méi)有出現(xiàn)在書(shū)中);在這種模式中,一個(gè)事件最初是由一個(gè)蹦床對(duì)象接收,這樣叫因?yàn)樗⒖谭磸?#xff0c;或重定向,處理到目標(biāo)對(duì)象的事件。
當(dāng)你需要反彈繼續(xù)處理另一個(gè)執(zhí)行上下文,可以通過(guò)前臺(tái)設(shè)計(jì)模式。當(dāng)你觀察一個(gè)通知,或者實(shí)現(xiàn)一個(gè)塊處理程序,或響應(yīng)一個(gè)消息,你要確保你的代碼在適當(dāng)?shù)膱?zhí)行上下文中執(zhí)行,可以實(shí)現(xiàn)前臺(tái)模式改變所必須做的工作,執(zhí)行上下文。前臺(tái)模式,在反彈任務(wù)數(shù)據(jù)處理之前,甚至可以執(zhí)行一些過(guò)濾或合并輸入數(shù)據(jù)。例如,可以收集數(shù)據(jù)分批進(jìn)入,然后在區(qū)間調(diào)度這些批次做其他處理。
一種常見(jiàn)的情況,接待員模式是有用的鍵值觀察。對(duì)一個(gè)模型對(duì)象的屬性的值的變化是通過(guò)KVO通知傳達(dá)給觀察者。然而,一個(gè)模型對(duì)象的變化可以在后臺(tái)線程上發(fā)生。這個(gè)結(jié)果在一個(gè)線程不匹配,因?yàn)橐粋€(gè)模型對(duì)象的狀態(tài)變化通常會(huì)導(dǎo)致更新用戶(hù)界面,這些都必須在主線程中發(fā)生。在這種情況下,你想重定向KVO通知主線程。
前臺(tái)設(shè)計(jì)模式的實(shí)踐
KVO通知調(diào)用由觀察者實(shí)現(xiàn)的observeValueForKeyPath:ofObject:change:context:方法。如果改變的屬性發(fā)生在輔助線程,該observeValueForKeyPath:ofObject:change:context:在同一個(gè)線程執(zhí)行的代碼。在這個(gè)模式中有中央的對(duì)象,前臺(tái)接待員,作為一個(gè)線程的中介。下圖說(shuō)明,接待對(duì)象被分配作為一個(gè)模型對(duì)象的屬性的觀察。前臺(tái)實(shí)現(xiàn)observeValueForKeyPath:ofObject:change:context:將收到的通知在輔助線程上的另一個(gè)執(zhí)行上下文的主要操作隊(duì)列,在這種情況下。屬性變化時(shí),接待員收到一個(gè)KVO通知。接待員立即增加一塊操作的主要操作隊(duì)列;塊包含指定的更新用戶(hù)界面的適當(dāng)代碼。
定義一個(gè)接待員類(lèi)具有元素需要添加本身作為一個(gè)觀察者的屬性,然后將一個(gè)KVO通知到一個(gè)更新的任務(wù)。因此,它必須知道它的觀察對(duì)象,該對(duì)象的屬性,這是觀察,什么更新任務(wù)執(zhí)行,什么隊(duì)列執(zhí)行它。下面代碼顯示初始聲明RCReceptionist類(lèi)及其實(shí)例變量。
@interface RCReceptionist : NSObject {id observedObject;NSString *observedKeyPath;RCTaskBlock task;NSOperationQueue *queue; }RCTaskBlock實(shí)例變量是下面這樣的block對(duì)象
typedef void (^RCTaskBlock)(NSString *keyPath, id object, NSDictionary *change);observeValueForKeyPath:ofObject:change:context: 方法的參數(shù)是相似的,參數(shù)類(lèi)聲明單例類(lèi)工廠方法,rctaskblock對(duì)象是一個(gè)參數(shù):
+ (id)receptionistForKeyPath:(NSString *)pathobject:(id)objqueue:(NSOperationQueue *)queuetask:(RCTaskBlock)task;它實(shí)現(xiàn)了該方法分配傳入的值來(lái)實(shí)例化被接待員對(duì)象創(chuàng)建的實(shí)例變量,對(duì)象為模型對(duì)象屬性的一個(gè)觀察者
+ (id)receptionistForKeyPath:(NSString *)path object:(id)obj queue:(NSOperationQueue *)queue task:(RCTaskBlock)task {RCReceptionist *receptionist = [RCReceptionist new];receptionist->task = [task copy];receptionist->observedKeyPath = [path copy];receptionist->observedObject = [obj retain];receptionist->queue = [queue retain];[obj addObserver:receptionist forKeyPath:pathoptions:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:0];return [receptionist autorelease]; }注意代碼復(fù)制的block對(duì)象而不是retaining它。因?yàn)閎ock可能是在棧上創(chuàng)建的,它必須被復(fù)制到堆,,KVO通知時(shí)它得存在于內(nèi)存中。
最后,observeValueForKeyPath:ofObject:change:context:方法參數(shù)的實(shí)現(xiàn):
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)objectchange:(NSDictionary *)change context:(void *)context {[queue addOperationWithBlock:^{task(keyPath, object, change);}]; }這個(gè)代碼在給定的運(yùn)行隊(duì)列進(jìn)行簡(jiǎn)單的查詢(xún)?nèi)蝿?wù),通過(guò)被觀察對(duì)象的task block,為改變屬性的關(guān)鍵路徑,和字典包含新value。任務(wù)是封裝在一個(gè)NSBlockOperation對(duì)象執(zhí)行任務(wù)隊(duì)列。
客戶(hù)端對(duì)象提供的block代碼,更新用戶(hù)界面創(chuàng)建一個(gè)接待員對(duì)象時(shí),如下面代碼,請(qǐng)注意,創(chuàng)建的接待對(duì)象時(shí),客戶(hù)端傳給在運(yùn)行隊(duì)列的數(shù)據(jù)block被執(zhí)行,在這種情況下的主要操作隊(duì)列。
RCReceptionist *receptionist = [RCReceptionist receptionistForKeyPath:@"value" object:model queue:mainQueue task:^(NSString *keyPath, id object, NSDictionary *change) {NSView *viewForModel = [modelToViewMap objectForKey:model];NSColor *newColor = [change objectForKey:NSKeyValueChangeNewKey];[[[viewForModel subviews] objectAtIndex:0] setFillColor:newColor];}];單例(Singleton)
單例設(shè)計(jì)模式確保一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。這類(lèi)跟蹤它的唯一實(shí)例,可以確保沒(méi)有其他實(shí)例可以被創(chuàng)建。單例類(lèi)是適當(dāng)?shù)那闆r下,對(duì)于提供一個(gè)全球性的資源是有意義的一個(gè)對(duì)象。
框架類(lèi)
幾個(gè)Cocoa框架類(lèi)是單例。他們包括NSFileManager,NSWorkspace,NSApplication以及在UIKit中,UIApplication。一個(gè)進(jìn)程是限制這些類(lèi)只有一個(gè)實(shí)例。當(dāng)客戶(hù)端請(qǐng)求一個(gè)實(shí)例的類(lèi),它獲取一個(gè)共享實(shí)例,這是建立在第一次請(qǐng)求時(shí)懶創(chuàng)建的。
使用和限制
使用通過(guò)單例類(lèi)返回的共享實(shí)例,和使用非單例類(lèi)的一個(gè)實(shí)例沒(méi)什么不同,只是你無(wú)法copying、retaining或者releasing它(有關(guān)方法重新實(shí)現(xiàn)零操作)。如果情況需要可以創(chuàng)建你自己的單例類(lèi)
模板方法
模板方法設(shè)計(jì)模式定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類(lèi)。模板方法模式讓子類(lèi)重新定義算法中的某些步驟而不改變算法的結(jié)構(gòu)。
重寫(xiě)框架方法
模板方法模式是一個(gè)基本的Cocoa設(shè)計(jì),而且面向?qū)ο罂蚣苁呛芷毡榈摹T贑ocoa的模式讓一個(gè)程序把自己的自定義組件為一個(gè)算法,但框架組件決定何時(shí)需要它們。Cocoa類(lèi)的編程接口通常包括由子類(lèi)重寫(xiě)方法。在運(yùn)行時(shí),框架調(diào)用這些所謂的通用方法在某些點(diǎn)的任務(wù)執(zhí)行。一般的方法提供了一種用于自定義代碼提供程序特定的行為和數(shù)據(jù)的任務(wù)執(zhí)行和協(xié)調(diào)的框架類(lèi)結(jié)構(gòu)。
使用和限制
利用Cocoa改編的模板方法模式,你必須創(chuàng)建一個(gè)類(lèi)的子類(lèi)并重寫(xiě)這些方法,框架調(diào)用插入應(yīng)用程序特定的輸入方法是執(zhí)行。如果你寫(xiě)自己的框架,你應(yīng)該包括在設(shè)計(jì)模式。
總結(jié)
以上是生活随笔為你收集整理的Cocoa设计模式(iOS常用设计模式) Cocoa Design Patterns的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Bad Request This com
- 下一篇: XML实现异构数据库间转换的实现与分析