關(guān)于資源
適用于計算機程序的資源是與程序可執(zhí)行代碼相關(guān)的數(shù)據(jù)文件八千。資源可以通過將代碼之外的復雜數(shù)據(jù)集或圖形內(nèi)容創(chuàng)建到更合適的工具中來簡化您必須編寫的代碼。例如燎猛,使用代碼逐個像素地創(chuàng)建圖像恋捆,在圖像編輯器中創(chuàng)建它們的效率(和實用性)要高得多。為了利用資源重绷,所有的代碼都必須在運行時加載它并使用它沸停。
除了簡化代碼之外,資源也是所有應用程序的國際化流程的一個親密的部分昭卓。您可以將應用程序中的字符串和其他用戶可見內(nèi)容硬編碼愤钾,而不是將內(nèi)容放在外部資源文件中瘟滨。本地化您的應用程序然后成為為每種支持的語言創(chuàng)建每個資源文件的新版本的簡單過程。在OS X和iOS中使用的捆綁機制提供了組織本地化資源并促進加載與用戶首選語言匹配的資源文件的方式能颁。
本文檔提供有關(guān)OS X和iOS支持的資源類型的信息杂瘸,以及如何在代碼中使用這些資源。本文檔不關(guān)注資源創(chuàng)建過程伙菊。大多數(shù)資源是使用第三方應用程序或/ Developer / Applications目錄中提供的開發(fā)人員工具創(chuàng)建的败玉。此外,雖然本文檔涉及應用程序中的資源使用镜硕,但是該信息也適用于其他類型的捆綁可執(zhí)行文件绒怨,包括框架和插件。
在閱讀本文檔之前谦疾,您應該熟悉應用程序包強加的組織結(jié)構(gòu)南蹂。了解此結(jié)構(gòu)可以更輕松地組織和查找應用程序使用的資源文件。有關(guān)捆綁結(jié)構(gòu)的信息念恍,請參閱捆綁編程指南六剥。
概述
應用程序可以包含許多類型的資源,但有幾種可由iOS和OS X直接支持峰伙。
Nib文件存儲應用程序用戶界面的對象
Nib文件是用于創(chuàng)建iOS和Mac應用程序的典型資源類型疗疟。 nib文件是一個數(shù)據(jù)存檔,其基本上包含要在運行時重新創(chuàng)建的一組凍干對象瞳氓。 Nib文件最常用于存儲預配置的窗口策彤,視圖和其他面向視覺的對象,但它們也可以存儲非可視對象(如控制器)匣摘。
您可以使用Interface Builder編輯Xcode中的nib文件店诗,該文件提供了組裝對象的圖形編輯器。當您隨后將nib文件加載到應用程序中時音榜,nib加載代碼會實例化文件中的每個對象庞瘸,并將其還原到您在Interface Builder中指定的狀態(tài)。因此赠叼,您在Interface Builder中看到的確實是您在運行時在應用程序中獲得的擦囊。
相關(guān)章節(jié):Nib文件
包含可本地化文本的字符串資源
文本是大多數(shù)用戶界面的突出部分,也是受本地化更改影響最大的資源嘴办。 iOS和OS X不是將文本硬編碼到代碼中瞬场,而是支持在字符串文件中存儲用戶可見文本,這些文本文件包含用于應用程序的一組字符串資源的人機閱讀文本文件(UTF-16編碼) 涧郊。 (使用多個“字符串”是有意的贯被,并且由于該類型的文件使用的.strings文件擴展名。)字符串文件大大簡化了國際化和本地化過程,允許您編寫一次代碼刃榨,然后正確加載來自資源文件的本地化文本可以輕松更改弹砚。
Core Foundation和Foundation框架提供了從字符串文件加載文本的功能。使用這些工具的應用程序也可以利用Xcode附帶的工具在整個開發(fā)過程中生成和維護這些資源文件枢希。
相關(guān)章節(jié):字符串資源
圖像桌吃,聲音和電影表示預呈現(xiàn)的內(nèi)容
圖像,聲音和電影資源在iOS和Mac應用程序中發(fā)揮重要作用苞轿。圖像負責創(chuàng)建每個操作系統(tǒng)使用的獨特視覺風格;它們還幫助簡化復雜視覺元素的繪圖代碼茅诱。聲音和電影文件同樣有助于增強應用程序的整體用戶體驗,同時簡化創(chuàng)建體驗所需的代碼搬卒。這兩種操作系統(tǒng)都為您的應用程序中加載和呈現(xiàn)這些類型的資源提供了廣泛的支持瑟俭。
相關(guān)章節(jié):圖像,聲音和視頻資源
屬性列表和數(shù)據(jù)文件將數(shù)據(jù)與代碼分開
屬性列表文件是用于存儲字符串契邀,數(shù)字摆寄,布爾值,日期和原始數(shù)據(jù)值的結(jié)構(gòu)化文件坯门。使用數(shù)組和字典結(jié)構(gòu)組織文件中的數(shù)據(jù)項微饥,大多數(shù)項與唯一鍵相關(guān)聯(lián)。系統(tǒng)使用屬性列表來存儲簡單的數(shù)據(jù)集古戴。例如欠橘,幾乎所有應用程序中的Info.plist文件都是屬性列表文件的示例。您還可以使用屬性列表文件進行簡單的數(shù)據(jù)存儲需求现恼。
除了屬性列表之外肃续,OS X還支持特定用途的一些特殊結(jié)構(gòu)化文件。例如叉袍,AppleScript數(shù)據(jù)和用戶幫助使用特殊格式的數(shù)據(jù)文件進行存儲始锚。您還可以創(chuàng)建自己的自定義數(shù)據(jù)文件。
相關(guān)章節(jié):數(shù)據(jù)資源文件
iOS支持特定于設(shè)備的資源
在iOS 4.0及更高版本中畦韭,可以將個別資源文件標記為僅在特定類型的設(shè)備上可用疼蛾。此功能簡化了為通用應用程序編寫的代碼。而不是創(chuàng)建單獨的代碼路徑來加載一個版本的資源文件的iPhone和iPad的不同版本的文件艺配,您可以讓捆綁加載例程選擇正確的文件。所有你需要做的是適當?shù)孛馁Y源文件衍慎。
要將資源文件與特定設(shè)備相關(guān)聯(lián)转唉,請將自定義修飾符字符串添加到其文件名。包含此修飾符字符串會產(chǎn)生以下格式的文件名:
<基本名稱> <設(shè)備> <filename_extension>
<basename>字符串表示資源文件的原始名稱稳捆。它也代表您從代碼訪問文件時使用的名稱赠法。類似地,<filename_extension>字符串是用于標識文件類型的標準文件擴展名。 <device>字符串是一個區(qū)分大小寫的字符串砖织,可以是以下值之一:
?ipad - 資源應該僅在iPad設(shè)備上加載款侵。
?iphone - 資源應該僅在iPhone或iPod touch設(shè)備上加載。
您可以將設(shè)備修飾符應用于任何類型的資源文件侧纯。例如新锈,假設(shè)您有一個名為MyImage.png的圖像。要為iPad和iPhone指定不同版本的圖像眶熬,您將創(chuàng)建名稱為MyImage?ipad.png和MyImage?iphone.png的資源文件妹笆,并將它們都包含在您的包中。要加載圖像娜氏,您將繼續(xù)在代碼中將資源引用為MyImage.png拳缠,并讓系統(tǒng)選擇適當?shù)陌姹荆缦滤荆?/p>
UIImage * anImage = [UIImage imageNamed:@“MyImage.png”];
在iPhone或iPod touch設(shè)備上贸弥,系統(tǒng)加載MyImage?iphone.png資源文件窟坐,而在iPad上,它會加載MyImage?ipad.png資源文件绵疲。如果找不到資源的特定于設(shè)備的版本哲鸳,則系統(tǒng)將退回尋找具有原始文件名的資源,在上述示例中最岗,該資源將是名為MyImage.png的映像帕胆。
也可以看看
以下Apple Developer文檔在概念上與資源編程指南有關(guān):
“捆綁編程指南”介紹了應用程序用于存儲可執(zhí)行代碼和資源的捆綁結(jié)構(gòu)。
國際化和本地化指南描述了準備應用程序(及其資源)以翻譯成其他語言的過程般渡。
章節(jié)在Xcode中構(gòu)建用戶界面概述描述了編輯nib文件資源的工具懒豹。
“屬性列表編程指南”介紹了將屬性列表資源文件加載到Cocoa應用程序中的工具。
Core Foundation的屬性列表編程主題描述了將屬性列表資源文件加載到基于C的應用程序中的工具驯用。
Nib文件
Nib文件在OS X和iOS中創(chuàng)建應用程序中起著重要的作用脸秽。使用nib文件,您可以使用Xcode(而不是以編程方式)以圖形方式創(chuàng)建和操作用戶界面蝴乔。因為您可以立即查看更改的結(jié)果记餐,您可以快速嘗試不同的布局和配置。您也可以稍后更改用戶界面的許多方面薇正,而無需重寫任何代碼片酝。
對于使用AppKit或UIKit框架構(gòu)建的應用程序,nib文件具有重要意義挖腰。這兩個框架都支持使用nib文件來布局窗口雕沿,視圖和控件,并將這些項目與應用程序的事件處理代碼集成在一起猴仑。 Xcode與這些框架配合使用审轮,可幫助您將用戶界面的控件連接到項目中對這些控件進行響應的對象。該集成顯著減少了加載nib文件后所需的設(shè)置量,并且稍后可以輕松更改代碼和用戶界面之間的關(guān)系疾渣。
注意:盡管您可以在不使用nib文件的情況下創(chuàng)建Objective-C應用程序篡诽,但這樣做非常罕見,不推薦使用榴捡。根據(jù)您的應用程序杈女,避免nib文件可能需要您替換大量的框架行為才能實現(xiàn)與使用nib文件相同的結(jié)果。
Nib文件的解剖
一個nib文件描述了應用程序的用戶界面的視覺元素薄疚,包括窗口碧信,視圖,控件等等街夭。它還可以描述非可視元素砰碴,例如應用程序中管理窗口和視圖的對象。最重要的是板丽,nib文件與Xcode中的配置完全相同呈枉。在運行時,這些描述用于在應用程序中重新創(chuàng)建對象及其配置埃碱。當您在運行時加載nib文件時猖辫,您將獲得Xcode文檔中對象的精確副本。 nib加載代碼實例化對象砚殿,配置它們啃憎,并重新建立您在nib文件中創(chuàng)建的任何對象間連接。
以下部分描述了如何組織與AppKit和UIKit框架一起使用的nib文件辛萍,在其中找到的對象類型以及如何有效地使用這些對象。
關(guān)于您的接口對象
接口對象是添加到nib文件來實現(xiàn)用戶界面的對象羡藐。當在運行時加載一個筆尖時贩毕,接口對象是由nib加載代碼實際實例化的對象。大多數(shù)新的nib文件默認情況下至少有一個接口對象仆嗦,通常是窗口或菜單資源辉阶,并且您可以在界面設(shè)計中添加更多的接口對象到nib文件。這是nib文件中最常見的對象類型瘩扼,通常是為什么首先創(chuàng)建nib文件谆甜。
除了表示視覺對象,如窗口集绰,視圖店印,控件和菜單之外,界面對象還可以表示非視覺對象倒慧。在幾乎所有情況下,添加到nib文件的非可視對象都是您的應用程序用于管理可視對象的額外控制器對象。雖然您可以在應用程序中創(chuàng)建這些對象纫谅,但將其添加到nib文件并將其配置在那里通常更為方便炫贤。 Xcode提供了一個通用對象,您可以在將控制器和其他非可視對象添加到nib文件時特別使用付秕。它還提供了通常用于管理Cocoa綁定的控制器對象兰珍。
關(guān)于文件的所有者
nib文件中最重要的對象之一是File的Owner對象。與接口對象不同询吴,文件所有者對象是一個占位符對象掠河,在加載nib文件時不會創(chuàng)建。相反猛计,您可以在代碼中創(chuàng)建此對象唠摹,并將其傳遞給nib加載代碼。這個對象是如此重要的原因是它是應用程序代碼和nib文件內(nèi)容之間的主要鏈接奉瘤。更具體地說勾拉,它是負責nib文件內(nèi)容的控制器對象。
在Xcode中盗温,您可以在文件的所有者和nib文件中的其他接口對象之間創(chuàng)建連接藕赞。加載nib文件時,nib加載代碼將使用您指定的替換對象重新創(chuàng)建這些連接卖局。這允許您的對象引用nib文件中的對象并自動從接口對象接收消息斧蜕。
關(guān)于第一個響應者
在nib文件中,F(xiàn)irst Responder是一個占位符對象砚偶,表示應用程序動態(tài)確定的響應器鏈中的第一個對象批销。因為應用程序的響應者鏈無法在設(shè)計時確定,所以第一響應者占位符充當需要針對應用程序的響應者鏈的任何操作消息的備用目標蟹演。菜單項通常針對第一響應者占位符风钻。例如,“窗口”菜單中的“最小化”菜單項隱藏應用程序中的最前面的窗口酒请,而不僅僅是特定的窗口骡技,“復制”菜單項應該復制當前選擇,而不僅僅是單個控件或視圖的選擇羞反。應用程序中的其他對象也可以針對第一響應者布朦。
當您將nib文件加載到內(nèi)存中時,您無需管理或替換第一個響應程序占位符對象昼窗。 AppKit和UIKit框架根據(jù)應用程序的當前配置自動設(shè)置和維護第一個響應者是趴。
有關(guān)響應者鏈的更多信息以及如何用于在基于AppKit的應用程序中分派事件,請參閱“事件處理指南”中的事件體系結(jié)構(gòu)澄惊。有關(guān)iPhone應用程序中的響應者鏈和處理操作的信息唆途,請參閱UIKit應用程序的事件處理指南富雅。
關(guān)于頂級對象
當您的程序加載nib文件時,Cocoa會重新創(chuàng)建Xcode中創(chuàng)建的對象的整個圖形肛搬。該對象圖包括在nib文件中找到的所有窗口没佑,視圖,控件温赔,單元格蛤奢,菜單和自定義對象。頂級對象是不具有父對象的這些對象的子集陶贼。頂級對象通常僅包含添加到nib文件中的窗口啤贩,菜單欄和自定義控制器對象。 (諸如文件所有者拜秧,第一響應者和應用程序的對象是占位符對象痹屹,并不被視為頂級對象。)
通常腹纳,您使用文件的所有者對象中的插座來存儲對nib文件的頂級對象的引用痢掠。但是,如果不使用插座嘲恍,則可以直接從nib加載例程檢索頂層對象足画。您應該始終保持一個指向這些對象的指針,因為您的應用程序負責釋放它們后佃牛,使用它們淹辞。有關(guān)加載時的nib對象行為的更多信息,請參閱從Nib文件管理對象的生命周期俘侠。
關(guān)于圖像和聲音資源
在Xcode中象缀,您可以從nib文件的內(nèi)容中引用外部圖像和聲音資源。一些控件和視圖能夠顯示圖像或播放聲音作為默認配置的一部分爷速。 Xcode庫提供對Xcode項目的圖像和聲音資源的訪問央星,以便您可以將nib文件鏈接到這些資源。 nib文件不直接存儲這些資源惫东。相反莉给,它存儲資源文件的名稱,以便稍后可以找到nib加載代碼廉沮。
加載包含圖像或聲音資源引用的nib文件時颓遏,nib加載代碼將實際的圖像或聲音文件讀入內(nèi)存并緩存它。在OS X中滞时,圖像和聲音資源存儲在命名的緩存中叁幢,以便以后可以在需要時訪問它們。在iOS中坪稽,只有映像資源存儲在命名緩存中曼玩。要訪問圖像鳞骤,請使用NSImage或UIImage的imageNamed:方法,具體取決于您的平臺演训。要在OS X中訪問緩存的聲音弟孟,請使用NSSound的soundNamed:方法。
Nib文件設(shè)計指南
創(chuàng)建nib文件時样悟,請仔細考慮如何打算使用該文件中的對象。一個非常簡單的應用程序可能能夠?qū)⑺杏脩艚缑娼M件存儲在單個nib文件中庭猩,但對于大多數(shù)應用程序窟她,最好將組件分布在多個nib文件中。創(chuàng)建較小的nib文件可讓您立即加載您需要的界面部分蔼水。它們還可以更容易地調(diào)試您可能遇到的任何問題震糖,因為找不到問題的地方較少。
創(chuàng)建nib文件時趴腋,請牢記以下準則:
設(shè)計你的nib文件與懶加載吊说。計劃加載僅包含您需要的對象的nib文件。
在OS X應用程序的主要nib文件中优炬,請考慮僅將應用程序菜單欄和可選應用程序委托對象存儲在nib文件中颁井。避免在應用程序啟動后包括任何不會使用的窗口或用戶界面元素。相反蠢护,將這些資源放在單獨的nib文件中雅宾,并在啟動后根據(jù)需要加載它們。
將重復的用戶界面組件(如文檔窗口)存儲在單獨的nib文件中葵硕。
對于僅偶爾使用的窗口或菜單眉抬,將其存儲在單獨的nib文件中。通過將其存儲在單獨的nib文件中懈凹,只有在實際使用時才將資源加載到內(nèi)存中蜀变。
使文件所有者成為nib文件外的任何單一聯(lián)系人;請參閱訪問Nib文件的內(nèi)容。
Nib對象生命周期
當nib文件加載到內(nèi)存中時介评,nib加載代碼需要幾個步驟來確保nib文件中的對象被正確創(chuàng)建和初始化库北。了解這些步驟可以幫助您編寫更好的控制器代碼來管理用戶界面。
對象加載過程
當您使用NSNib或NSBundle的方法加載和實例化nib文件中的對象時威沫,底層的nib加載代碼執(zhí)行以下操作:
它將nib文件和任何引用的資源文件的內(nèi)容加載到內(nèi)存中:
整個nib對象圖的原始數(shù)據(jù)被加載到內(nèi)存中贤惯,但不是未歸檔赦肃。
與nib文件相關(guān)聯(lián)的任何自定義圖像資源都將被加載并添加到Cocoa圖像緩存中;請參閱關(guān)于圖像和聲音資源裙戏。
與nib文件相關(guān)聯(lián)的任何自定義聲音資源都被加載并添加到Cocoa聲音緩存;請參閱關(guān)于圖像和聲音資源。
它取消歸檔nib對象圖數(shù)據(jù)并實例化對象绢彤。如何初始化每個新對象取決于對象的類型及其在歸檔中的編碼方式烟很。 nib加載代碼使用以下規(guī)則(按順序)來確定要使用的初始化方法颈墅。
默認情況下蜡镶,對象會收到一個initWithCoder:消息。
在OS X中恤筛,標準對象列表包括系統(tǒng)提供的視圖官还,單元格,菜單和視圖控制器毒坛,并且在默認的Xcode庫中可用望伦。它還包括使用自定義插件添加到庫的任何第三方對象。即使您更改了這樣一個對象的類煎殷,Xcode將標準對象編碼到nib文件中屯伞,然后在對象被取消存檔時通知歸檔器在自定義類中交換。
在iOS中豪直,使用initWithCoder:方法初始化符合NSCoding協(xié)議的任何對象劣摇。這包括UIView和UIViewController的所有子類,無論它們是默認的Xcode庫或定義的自定義類的一部分弓乙。
OS X中的自定義視圖會收到一條initWithFrame:消息末融。
自定義視圖是NSView的子類,Xcode沒有可用的實現(xiàn)暇韧。通常勾习,這些是您在應用程序中定義并用于提供自定義可視內(nèi)容的視圖。自定義視圖不包括作為默認庫或集成第三方插件的一部分的標準系統(tǒng)視圖(如NSSlider)锨咙。
當它遇到自定義視圖時语卤,Xcode將一個特殊的NSCustomView對象編碼到你的nib文件中。自定義視圖對象包括構(gòu)建您指定的真實視圖子類所需的信息酪刀。在加載時粹舵,NSCustomView對象將一個alloc和initWithFrame:消息發(fā)送到真實的視圖類,然后交換自己生成的視圖對象骂倘。凈效果是真正的視圖對象處理在nib加載過程中的后續(xù)交互眼滤。
iOS中的自定義視圖不會使用initWithFrame:方法進行初始化。
除了上述步驟中描述的以外的自定義對象接收初始消息历涝。
它重新建立nib文件中對象之間的所有連接(動作诅需,插座和綁定)。這包括與文件所有者和其他占位符對象的連接荧库。建立連接的方法因平臺而異:
出口連接
在OS X中堰塌,筆尖加載代碼首先嘗試使用對象自己的方法重新連接插座。對于每個出口分衫,Cocoa查找一個形式為setOutletName的方法场刑,如果存在這樣的方法,則調(diào)用它蚪战。如果找不到這樣的方法牵现,Cocoa會在對象中搜索具有相應插座名稱的實例變量铐懊,并嘗試直接設(shè)置值。如果找不到實例變量瞎疼,則不會創(chuàng)建任何連接科乎。
設(shè)置出口還會為任何注冊的觀察者生成鍵值觀察(KVO)通知。這些通知可能在所有對象間連接重新建立之前發(fā)生贼急,并且在調(diào)用對象的任何awakeFromNib方法之前肯定會發(fā)生這些通知茅茂。
在iOS中,nib加載代碼使用setValue:forKey:方法重新連接每個出口竿裂。該方法類似地尋找適當?shù)脑L問器方法玉吁,并且在失敗時落后于其他方式。有關(guān)此方法如何設(shè)置值的更多信息腻异,請參閱其在NSKeyValueCoding協(xié)議參考中的描述。
在iOS中設(shè)置出口還會為任何注冊的觀察者生成KVO通知这揣。這些通知可能在所有對象間連接重新建立之前發(fā)生悔常,并且在調(diào)用對象的任何awakeFromNib方法之前肯定會發(fā)生這些通知。
動作連接
在OS X中给赞,nib加載代碼使用源對象的setTarget:和setAction:方法來建立與目標對象的連接机打。如果目標對象沒有響應action方法,則不會創(chuàng)建任何連接片迅。如果目標對象為零残邀,則該動作由響應者鏈處理。
在iOS中柑蛇,nib加載代碼使用UIControl對象的addTarget:action:forControlEvents:方法來配置操作芥挣。如果目標為零,則動作由響應者鏈處理耻台。
綁定
在OS X中空免,Cocoa使用源對象的bind:toObject:withKeyPath:options:方法來創(chuàng)建它與其目標對象之間的連接。
iOS中不支持綁定盆耽。
它將一個awakeFromNib消息發(fā)送到nib文件中定義匹配選擇器的相應對象:
在OS X中蹋砚,該消息被發(fā)送到定義該方法的任何接口對象。它也被發(fā)送到文件的所有者和定義它的任何占位符對象摄杂。
在iOS中坝咐,此消息僅發(fā)送到由nib加載代碼實例化的接口對象。它不發(fā)送到文件的所有者析恢,第一響應者或任何其他占位符對象墨坚。
它顯示在nib文件中啟用了“啟動時可見”屬性的任何窗口。
nib加載代碼調(diào)用對象的awakeFromNib方法的順序是不能保證的氮昧。在OS X中框杜,Cocoa嘗試最后調(diào)用File的Owner的awakeFromNib方法浦楣,但不能保證該行為。如果您需要在加載時進一步配置nib文件中的對象咪辱,則最適合的時間是在您的nib加載調(diào)用返回后振劳。在這一點上,所有的對象都被創(chuàng)建油狂,初始化并準備好使用历恐。
從Nib文件管理對象的生命周期
每次您要求NSBundle或NSNib類加載nib文件時,底層代碼會創(chuàng)建該文件中的對象的新副本并將其返回給您专筷。 (nib加載代碼不會從以前的加載嘗試中回收nib文件對象弱贼。)您需要確保在必要時保持新的對象圖,并在完成之后將其取消磷蛹。通常需要對頂級對象的強引用以確保它們不被釋放;您不需要強烈引用圖表中較低的對象吮旅,因為它們由父母擁有,您應該盡可能減少創(chuàng)建強引用周期的風險味咳。
從實際的角度來看庇勃,iOS和OS X的出口應該被定義為聲明的屬性。 Outlets通常應該是弱的槽驶,除了那些從File的所有者到頂級對象的nib文件(或在iOS责嚷,一個故事板場景中)應該是強的。 因此掂铐,您創(chuàng)建的出口通常應該很弱罕拂,因為:
例如,您創(chuàng)建到視圖控制器視圖或窗口控制器窗口的子視圖的出口是不暗示所有權(quán)的對象之間的任意引用全陨。
強大的插座經(jīng)常由框架類指定(例如爆班,UIViewController的視圖插座或NSWindowController的窗口)。
@property (weak) IBOutlet MyView *viewContainerSubview;
@property (strong) IBOutlet MyOtherClass *topLevelObject;
注意:在OS X中烤镐,并非所有類都支持弱引用 - 請參閱轉(zhuǎn)換到ARC發(fā)行說明蛋济。 在不能指定的情況下,您應該使用
assign:
@property (assign) IBOutlet NSTextView *textView;
出口通常被認為是定義類別的私人; 除非有理由公開揭露屬性炮叶,否則隱藏屬性聲明類擴展名碗旅。 例如:
// MyClass.h
@interface MyClass : MySuperclass
@end
// MyClass.m
@interface MyClass ()
@property (weak) IBOutlet MyView *viewContainerSubview;
@property (strong) IBOutlet MyOtherClass *topLevelObject;
@end
這些模式擴展到從容器視圖到其子視圖的引用,您必須考慮對象圖的內(nèi)部一致性镜悉。例如祟辟,在表視圖單元格的情況下,特定子視圖的出口通常應該通常較弱侣肄。如果表視圖包含圖像視圖和文本視圖旧困,那么這些視圖仍然有效,只要它們是表視圖單元本身的子視圖。
當出口被認為擁有參考對象時吼具,出口應變?yōu)閺姡?/p>
如前所述僚纷,通常情況下,文件的所有者級別的對象在nib文件中經(jīng)常被認為是由文件所有者擁有的拗盒。
在某些情況下怖竭,您可能需要一個來自nib文件的對象存在其原始容器之外。例如陡蝇,您可能有一個視圖的出口痊臭,可以臨時從其初始視圖層次結(jié)構(gòu)中刪除,因此必須獨立進行維護登夫。
您希望被子類化的類(特別是抽象類)公開暴露出來广匙,以便它們可以被子類(例如UIViewController的視圖插件)適當?shù)厥褂谩H绻谕M者需要與物業(yè)相互作用恼策,則出口也可能會暴露出來;例如鸦致,表視圖單元格可能會暴露子視圖。在后一種情況下涣楷,公開一個以私有重新定義的只讀公共出口可能是可讀的蹋凝,例如:
// MyClass.h
@interface MyClass : UITableViewCell
@property (weak, readonly) MyType *outletName;
@end
// MyClass.m
@interface MyClass ()
@property (weak, readwrite) IBOutlet MyType *outletName;
@end
OS X中的頂級對象可能需要特殊處理
由于歷史原因,在OS X中总棵,將創(chuàng)建一個nib文件中的頂級對象,并附加一個引用計數(shù)改含。應用套件提供了幾個功能情龄,可幫助確保正確釋放nib對象:
NSWindow對象(包括面板)有一個isReleasedWhenClosed屬性,如果設(shè)置為YES捍壤,則會在窗口關(guān)閉時指示窗口釋放自身(以及其視圖層次結(jié)構(gòu)中的所有相關(guān)對象)骤视。在nib文件中,您可以通過Xcode檢查器的“屬性”窗格中的“釋放時關(guān)閉”復選框來設(shè)置此選項鹃觉。
如果文件的nib文件的所有者是NSWindowController對象(在基于文檔的應用程序中的文檔nib中的默認值)专酗,請記住NSDocument管理NSWindowController的一個實例)或NSViewController對象,它會自動處理其管理的窗口盗扇。
如果文件的所有者不是NSWindowController或NSViewController的實例祷肯,那么您需要自己遞減頂級對象的引用計數(shù)。您必須將頂級對象的引用轉(zhuǎn)換為Core Foundation類型并使用CFRelease疗隶。 (如果您不希望有所有頂級對象的插座佑笋,可以使用NSNib類的instantiateNibWithOwner:topLevelObjects:方法來獲取一個nib文件頂級對象的數(shù)組。)
行動方法
一般來說斑鼻,操作方法(參見OS X中的目標操作或iOS中的目標操作)是通常由nib文件中另一個對象調(diào)用的方法蒋纬。 Action方法使用類型限定符IBAction,它用于代替void返回類型,將聲明的方法標記為一個操作蜀备,以便Xcode知道它关摇。
@interface MyClass
- (IBAction)myActionMethod:(id)sender;
@end
您可以選擇將操作方法視為對您的類是私有的,因此不會在public @interface中聲明它們碾阁。 (因為Xcode解析實現(xiàn)文件输虱,所以不需要在標題中聲明它們。)
// MyClass.h
@interface MyClass
@end
// MyClass.m
@implementation MyClass
- (IBAction)myActionMethod:(id)sender {
// Implementation.
}
@end
您通常不應以編程方式調(diào)用操作方法瓷蛙。 如果您的類需要執(zhí)行與action方法相關(guān)聯(lián)的工作悼瓮,那么您應該將實現(xiàn)應用到另一種由action方法調(diào)用的方法中。
// MyClass.h
@interface MyClass
@end
// MyClass.m
@interface MyClass (PrivateMethods)
- (void)doSomething;
- (void)doWorkThatRequiresMeToDoSomething;
@end
@implementation MyClass
- (IBAction)myActionMethod:(id)sender {
[self doSomething];
}
- (void)doSomething {
// Implementation.
}
- (void)doWorkThatRequiresMeToDoSomething {
// Pre-processing.
[self doSomething];
// Post-processing.
}
@end
內(nèi)置支持Nib文件
AppKit和UIKit框架都提供了一定量的自動化行為來加載和管理應用程序中的nib文件艰猬。這兩個框架都提供了用于加載應用程序主要nib文件的基礎(chǔ)設(shè)施横堡。此外,AppKit框架提供了通過NSDocument和NSWindowController類加載其他nib文件的支持冠桃。以下部分介紹了nib文件的內(nèi)置支持命贴,如何利用它們,以及如何在自己的應用程序中修改該支持食听。
應用程序加載主Nib文件
應用程序的大多數(shù)Xcode項目模板都已預先配置了一個主要的nib文件已經(jīng)到位胸蛛。所有您需要做的是修改nib文件中的默認nib文件并構(gòu)建您的應用程序。在啟動時樱报,應用程序的默認配置數(shù)據(jù)告訴應用程序?qū)ο笳业皆搉ib文件葬项,以便它可以加載它。在基于AppKit和UIKit的應用程序中迹蛤,此配置數(shù)據(jù)位于應用程序的Info.plist文件中民珍。首次加載應用程序時,默認應用程序啟動代碼會在Info.plist文件中查找NSMainNibFile密鑰盗飒。如果找到它嚷量,它會在應用程序包中查找一個nib文件,其名稱(帶或不帶文件擴展名)與該鍵的值匹配并加載它逆趣。
每個視圖控制器管理其自己的Nib文件
UIViewController(iOS)和NSViewController(OS X)類支持自動加載其關(guān)聯(lián)的nib文件蝶溶。如果在創(chuàng)建視圖控制器時指定nib文件,則當您嘗試訪問視圖控制器的視圖時宣渗,該nib文件會自動加載抖所。視圖控制器和nib文件對象之間的任何連接都將自動創(chuàng)建,在iOS中落包,當視圖最終加載并顯示在屏幕上時部蛇,UIViewController對象還會收到其他通知。為了更好地管理內(nèi)存咐蝇,UIViewController類還可以在低內(nèi)存條件下處理卸載其nib文件(如適用)涯鲁。
有關(guān)如何使用UIViewController類及其配置方式的更多信息巷查,請參閱“用于iOS的View Controller編程指南”。
文檔和窗口控制器加載相關(guān)的Nib文件
在AppKit框架中抹腿,NSDocument類與默認窗口控制器一起加載包含文檔窗口的nib文件岛请。 NSDocument的windowNibName方法是一種方便的方法,您可以使用它來指定包含相應文檔窗口的nib文件警绩。創(chuàng)建新文檔時崇败,文檔對象將您指定的nib文件名傳遞給默認的窗口控制器對象,該對象加載并管理nib文件的內(nèi)容肩祥。如果您使用Xcode提供的標準模板后室,您唯一需要做的是將文檔窗口的內(nèi)容添加到nib文件。
NSWindowController類還提供自動支持加載nib文件混狠。如果以編程方式創(chuàng)建自定義窗口控件岸霹,則可以選擇使用NSWindow對象或nib文件的名稱初始化它們。如果選擇后一個選項将饺,NSWindowController類會在客戶端首次嘗試訪問窗口時自動加載指定的nib文件贡避。之后,窗口控制器將窗口保持在內(nèi)存中;即使窗口的“關(guān)閉時釋放”屬性被設(shè)置予弧,它也不會從nib文件重新加載它刮吧。
重要:當使用NSWindowController或NSDocument自動加載窗口時,重要的是您的nib文件配置正確掖蛤。這兩個類都包括一個窗口杀捻,您必須連接到您要管理的窗口。如果不將此插座連接到窗口對象蚓庭,則nib文件已加載水醋,但文檔或窗口控制器不顯示窗口。有關(guān)Cocoa文檔體系結(jié)構(gòu)的更多信息彪置,請參閱“基于文檔的應用程序編程指南”。
以編程方式加載Nib文件
OS X和iOS都提供了將nib文件加載到應用程序中的便利方法蝇恶。 AppKit和UIKit框架都在NSBundle類上定義了支持加載nib文件的附加方法拳魁。此外,AppKit框架還提供了NSNib類撮弧,它提供與NSBundle類似的nib加載行為潘懊,但提供了在特定情況下可能有用的一些其他優(yōu)點。
在計劃應用程序時贿衍,請確保手動加載的任何nib文件都以簡化加載過程的方式進行配置授舟。為文件所有者選擇一個適當?shù)膶ο蟛⒈3帜愕膎ib文件很小可以大大提高它們的易用性和內(nèi)存效率。有關(guān)配置nib文件的更多提示贸辈,請參閱Nib文件設(shè)計指南释树。
使用NSBundle加載Nib文件
AppKit和UIKit框架在NSBundle類(使用Objective-C類別)上定義了其他方法來支持加載nib文件資源。兩種平臺之間使用方法的語義與方法的語法不同。在AppKit中奢啥,通常有更多的選項可以訪問bundle秸仙,因此還有更多的方法可以從這些bundle加載nib文件。在UIKit中桩盲,應用程序只能從主包裝載nib文件寂纪,因此需要較少的選項。兩種平臺上可用的方法如下:
AppKit
loadNibNamed:owner:class方法
loadNibFile:externalNameTable:withZone:class方法
loadNibFile:externalNameTable:withZone:instance方法
UIKit
loadNibNamed:owner:options:instance方法
每當加載nib文件時赌结,都應該始終指定一個對象作為該nib文件的文件所有者捞蛋。文件所有者的作用是重要的。它是運行代碼和即將在內(nèi)存中創(chuàng)建的新對象之間的主界面柬姚。所有的nib加載方法提供了一種方法來直接指定文件的所有者拟杉,或者作為選項字典中的參數(shù)。
AppKit和UIKit框架處理nib加載的方式之間的語義差異之一就是頂層的nib對象返回到應用程序的方式伤靠。在AppKit框架中捣域,您必須使用loadNibFile:externalNameTable:withZone:methods顯式請求它們。在UIKit中宴合,loadNibNamed:owner:options:方法直接返回這些對象的數(shù)組焕梅。在這兩種情況下避免擔心頂層對象的最簡單的方法是將它們存儲在文件所有者對象的插座中(請參閱從Nib文件管理對象的生命周期)。
清單1-1顯示了一個簡單的例子卦洽,說明如何使用基于AppKit的應用程序中的NSBundle類加載nib文件贞言。 loadNibNamed:owner:方法返回后,可以開始使用任何引用nib文件對象的插座阀蒂。換句話說该窗,整個nib加載過程發(fā)生在該單個調(diào)用的限制內(nèi)。 AppKit框架中的nib加載方法返回一個布爾值蚤霞,以指示加載操作是否成功酗失。
清單1-1從當前bundle加載nib文件
- (BOOL)loadMyNibFile
{
// The myNib file must be in the bundle that defines self's class.
if (![NSBundle loadNibNamed:@"myNib" owner:self])
{
NSLog(@"Warning! Could not load myNib file.\n");
return NO;
}
return YES;
}
清單1-2顯示了如何在基于UIKit的應用程序中加載nib文件的示例。 在這種情況下昧绣,該方法將檢查返回的數(shù)組以查看nib對象是否已成功加載规肴。 (每個nib文件應至少有一個表示nib文件內(nèi)容的頂級對象。)此示例顯示了當文件所有者對象不包含占位符對象時的簡單情況夜畴。 有關(guān)如何指定其他占位符對象的示例拖刃,請參閱在加載時替換代理對象。
清單1-2在iPhone應用程序中加載nib
- (BOOL)loadMyNibFile
{
NSArray* topLevelObjs = nil;
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:@"myNib" owner:self options:nil];
if (topLevelObjs == nil)
{
NSLog(@"Error! Could not load myNib file.\n");
return NO;
}
return YES;
}
注意:如果您正在開發(fā)適用于iOS的通用應用程序贪绘,則可以使用特定于設(shè)備的命名約定自動為底層設(shè)備加載正確的nib文件兑牡。有關(guān)如何命名nib文件的更多信息,請參閱iOS支持特定于設(shè)備的資源税灌。
獲取Nib文件的頂級對象
獲取nib文件的頂級對象的最簡單方法是在File的Owner對象中定義插件以及用于訪問這些對象的setter方法(或更好的屬性)均函。此方法可確保頂層對象由對象保留亿虽,并始終對其進行引用。
清單1-3顯示了使用插座保留nib文件唯一頂級對象的簡化Cocoa類的接口和實現(xiàn)边酒。在這種情況下经柴,nib文件中唯一的頂級對象是NSWindow對象。因為Cocoa中的頂級對象的初始保留計數(shù)為1墩朦,因此會包含一個額外的釋放消息坯认。這是很好的,因為在發(fā)出調(diào)用的時候氓涣,該屬性已被保留在窗口中牛哺。您不會希望以這種方式在iPhone應用程序中釋放頂級對象。
清單1-3使用出口來獲取頂級對象
// Class interface.
@interface MyController : NSObject
- (void)loadMyWindow;
@end
// Private class extension.
@interface MyController ()
@property (strong) IBOutlet NSWindow *window;
@end
// Class implementation
@implementation MyController
- (void)loadMyWindow {
[NSBundle loadNibNamed:@"myNib" owner:self];
// The window starts off with a retain count of 1
// and is then retained by the property, so add an extra release.
NSWindow *window = self.window;
CFRelease(__bridge window);
}
@end
如果您不想使用出口來存儲對nib文件的頂級對象的引用劳吠,則必須在代碼中手動檢索這些對象引润。獲取頂級對象的技術(shù)因目標平臺而異。在OS X中痒玩,您必須明確要求對象淳附,而在iOS中,它們將自動返回給您蠢古。
清單1-4顯示了在OS X中獲取nib文件的頂級對象的過程奴曙。此方法將可變數(shù)組放入nameTable字典中,并將其與NSNibTopLevelObjects關(guān)鍵字相關(guān)聯(lián)草讶。 nib加載代碼查找此數(shù)組對象洽糟,如果存在,將頂級對象放在其中堕战。因為每個對象在添加到數(shù)組之前以保留計數(shù)為1開始坤溃,所以簡單地釋放數(shù)組也不足以釋放數(shù)組中的對象。因此嘱丢,此方法向每個對象發(fā)送發(fā)布消息薪介,以確保數(shù)組是唯一持有對它們的引用的實體。
清單1-4在運行時從nib文件獲取頂級對象
- (NSArray*)loadMyNibFile
{
NSBundle* aBundle = [NSBundle mainBundle];
NSMutableArray* topLevelObjs = [NSMutableArray array];
NSDictionary* nameTable = [NSDictionary dictionaryWithObjectsAndKeys:
self, NSNibOwner,
topLevelObjs, NSNibTopLevelObjects,
nil];
if (![aBundle loadNibFile:@"myNib" externalNameTable:nameTable withZone:nil])
{
NSLog(@"Warning! Could not load myNib file.\n");
return nil;
}
// Release the objects so that they are just owned by the array.
[topLevelObjs makeObjectsPerformSelector:@selector(release)];
return topLevelObjs;
}
獲取iPhone應用程序中的頂級對象要簡單得多越驻,如清單1-2所示昭灵。在UIKit框架中,NSBundle的loadNibNamed:owner:options:方法自動返回一個包含頂級對象的數(shù)組伐谈。另外,在返回數(shù)組之前试疙,對對象的保留計數(shù)進行調(diào)整诵棵,以便不需要向每個對象發(fā)送額外的釋放消息。返回的數(shù)組是對象的唯一所有者祝旷。
使用UINib和NSNib加載Nib文件
在要創(chuàng)建nib文件內(nèi)容的多個副本的情況下履澳,UINib(iOS)和NSNib(OS X)類提供更好的性能嘶窄。正常的nib加載過程涉及從磁盤讀取nib文件,然后實例化其包含的對象距贷。然而柄冲,使用UINib和NSNib類,從磁盤讀取nib文件一次忠蝗,并將內(nèi)容存儲在內(nèi)存中现横。因為它們在內(nèi)存中,創(chuàng)建連續(xù)的對象集需要更少的時間阁最,因為它不需要訪問磁盤戒祠。
使用UINib和NSNib類始終是一個兩步的過程。首先速种,您創(chuàng)建一個類的實例姜盈,并使用nib文件的位置信息進行初始化。其次配阵,您將實例化nib文件的內(nèi)容以將對象加載到內(nèi)存中馏颂。每次實例化nib文件時,都需要指定一個不同的File的Owner對象并接收一組新的頂級對象棋傍。
清單1-5顯示了使用OS X中的NSNib類加載nib文件的內(nèi)容的一種方法救拉。由instantiateNibWithOwner返回給您的數(shù)組:topLevelObjects:方法已經(jīng)自動釋放。如果您打算使用該陣列任何一段時間舍沙,您應該復制一份近上。
清單1-5使用NSNib加載nib文件
- (NSArray*)loadMyNibFile
{
NSNib* aNib = [[NSNib alloc] initWithNibNamed:@"MyPanel" bundle:nil];
NSArray* topLevelObjs = nil;
if (![aNib instantiateNibWithOwner:self topLevelObjects:&topLevelObjs])
{
NSLog(@"Warning! Could not load nib file.\n");
return nil;
}
// Release the raw nib data.
[aNib release];
// Release the top-level objects so that they are just owned by the array.
[topLevelObjs makeObjectsPerformSelector:@selector(release)];
// Do not autorelease topLevelObjs.
return topLevelObjs;
}
在加載時替換代理對象
在iOS中,可以創(chuàng)建除文件所有者之外的包括占位符對象的nib文件拂铡。代理對象表示在nib文件外部創(chuàng)建但與nib文件內(nèi)容有某種連接的對象壹无。代理通常用于支持iPhone應用程序中的導航控制器。當使用導航控制器時感帅,您通常將文件的所有者對象連接到某些常見對象(如應用程序委托)斗锭。因此,代理對象因此表示導航控制器對象層次結(jié)構(gòu)中已經(jīng)加載到內(nèi)存中的部分失球,因為它們是以編程方式創(chuàng)建的岖是,也可以從不同的nib文件加載。
注意:OS X nib文件不支持自定義占位符對象(文件所有者除外)实苞。
您添加到nib文件的每個占位符對象必須具有唯一的名稱豺撑。要為對象分配名稱,請選擇Xcode中的對象并打開檢查器窗口黔牵。檢查器的“屬性”窗格包含一個“名稱”字段聪轿,用于指定占位符對象的名稱。您分配的名稱應描述對象的行為或類型猾浦,但實際上它可以是任何您想要的陆错。
當您準備加載包含占位符對象的nib文件時灯抛,必須在調(diào)用loadNibNamed:owner:options:method時指定任何代理的替換對象。此方法的options參數(shù)接受附加信息的字典音瓷。您可以使用此字典傳遞有關(guān)占位符對象的信息对嚼。字典必須包含UINibExternalObjects鍵,其值是另一個包含每個占位符替換的名稱和對象的字典绳慎。
清單1-6顯示了一個applicationDidFinishLaunching:方法的示例版本纵竖,用于手動加載應用程序的主nib文件。因為應用程序的委托對象是由UIApplicationMain函數(shù)創(chuàng)建的偷线,所以該方法在主nib文件中使用占位符(名稱為“AppDelegate”)來表示該對象磨确。代理字典存儲占位符對象信息,并且選項字典包含該字典声邦。
清單1-6替換nib文件中的占位符對象
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
NSArray* topLevelObjs = nil;
NSDictionary* proxies = [NSDictionary dictionaryWithObject:self forKey:@"AppDelegate"];
NSDictionary* options = [NSDictionary dictionaryWithObject:proxies forKey:UINibExternalObjects];
topLevelObjs = [[NSBundle mainBundle] loadNibNamed:@"Main" owner:self options:options];
if ([topLevelObjs count] == 0)
{
NSLog(@"Warning! Could not load myNib file.\n");
return;
}
// Show window
[window makeKeyAndVisible];
}
有關(guān)loadNibNamed選項字典的更多信息:owner:options:方法乏奥,請參閱NSBundle UIKit添加參考。
訪問Nib文件的內(nèi)容
成功加載nib文件后亥曹,其內(nèi)容就可以立即使用邓了。如果您在文件所有者中配置了插座來指向nib文件對象,那么現(xiàn)在可以使用這些插座媳瞪。如果您沒有使用任何插座配置文件的所有者骗炉,則應確保以某種方式獲取對頂級對象的引用,以便稍后釋放它們蛇受。
因為插件在加載nib文件時填充實際對象句葵,所以隨后可以像您以編程方式創(chuàng)建的任何其他對象一樣使用插座。例如兢仰,如果您有一個指向窗口的插槽乍丈,則可以將窗口發(fā)送一個makeKeyAndOrderFront:消息,以在用戶屏幕上顯示該消息把将。完成使用nib文件中的對象后轻专,您必須像任何其他對象一樣釋放它們。
重要提示:完成這些對象后察蹲,您將負責釋放加載的任何nib文件的頂級對象请垛。不這樣做是許多應用程序中內(nèi)存泄漏的原因。釋放頂級對象后洽议,將nib文件中指向?qū)ο蟮娜魏纬隹诙记宄秊榱阕谑眨@是一個好主意。您應該清除與所有nib文件對象相關(guān)聯(lián)的出口亚兄,而不僅僅是頂級對象混稽。
連接Nib文件中的菜單項
OS X應用程序菜單欄中的項目通常需要與許多不同的對象進行交互,包括應用程序的文檔和窗口。問題是許多這些對象不能(或不應該)直接從主nib文件訪問荚坞。文件的主要nib文件的所有者始終設(shè)置為NSApplication類的一個實例。雖然您可能能夠在主要nib文件中實例化一些自定義對象菲盾,但這樣做是不切實際或不必要的颓影。在文檔對象的情況下,直接連接到特定文檔對象是不可能的懒鉴,因為文檔對象的數(shù)量可以動態(tài)地更改诡挂,甚至可以為零。
大多數(shù)菜單項將動作消息發(fā)送到以下之一:
一個總是處理命令的固定對象
動態(tài)對象临谱,如文檔或窗口
消息傳遞固定對象是一個相對簡單的過程璃俗,通常最好通過應用程序委托來處理。應用程序委托對象在運行應用程序時協(xié)助NSApplication對象悉默,并且是主要nib文件中正確屬于的少數(shù)對象之一城豁。如果菜單項是指應用程序級命令,則可以直接在應用程序委托中實現(xiàn)該命令抄课,或者只需讓代理將消息轉(zhuǎn)發(fā)到應用程序其他位置的相應對象唱星。
如果您有一個菜單項作用于最前面的窗口的內(nèi)容,則需要將菜單項鏈接到第一個響應者占位符對象跟磨。如果與菜單項相關(guān)聯(lián)的操作方法特定于您的一個對象(而不是由Cocoa定義)间聊,則必須在創(chuàng)建連接之前將該操作添加到第一個響應程序。
創(chuàng)建連接后抵拘,您需要在自定義類中實現(xiàn)操作方法哎榴。該對象還應實現(xiàn)validateMenuItem:方法,以在適當?shù)臅r間啟用菜單項僵蛛。有關(guān)響應者鏈如何處理命令的更多信息尚蝌,請參閱cocoa事件處理指南。