OC基礎

OC基礎編程技巧 程序員

正如它的名字所傳達的含義,Foundation 框架是全部 iOS 和 Mac OS X 編程所使用的基本工具。要成爲這兩個平臺上成功的開發者,必須對這套工具瞭如指掌。 編程

Foundation 框架定義了數量衆多的類以及協議,它們各司其職。但三種類和協議的地位更加突出,它們是最基本的部分: 設計模式

  • 根類和相關的協議。 根類,即  NSObject ,還伴有一個同名的協議。它肯定了全部 Objective-C 對象的基本接口和行爲。同時也有一些協議,其餘類能夠採用這些協議來拷貝這些類的實例並對編碼它們的狀態。
  • 數值類。 數值類可以產生一個實例(稱爲數值對象),也就是將字符串、數字、日期、二進制數據等基本類型數據封裝起來的面向對象包裝。
  • 羣體類。 羣體類的一個實例(稱爲羣體)管理着一組對象。區分不一樣類型的羣體就要看訪問它所包含的對象的方式是什麼。一般,羣體中包含的項目都是一系列數值對象。

羣體和數值對象是 Objective-C 編程中極其重要的內容,由於它們常常被用做方法的參數和返回值。 數組

根類和 Objective-C 對象

在類繼承中,根類不從其餘類繼承,同時全部其餘的類都最終繼承自根類。 NSObject  是 Objective-C 繼承中的根類。其餘類都從  NSObject  繼承一套基本的接口到 Objective-C 運行時體系中。這些類的實例又都是從  NSObject  繼承而得到 Objective-C 最根本的特性。 安全

但就其自身而言, NSObject  的實例作不了什麼有趣的事,頂多只是個對象而已。要使用更多屬性和邏輯來定製你的程序,就必須創造一個或多個繼承自  NSObject  的類,或者使用已有的直接或間接繼承自  NSObject  的類。 網絡

NSObject  採用了  NSObject  的協議,它聲明瞭一些附加方法,能夠被全部對象的接口使用。另外, NSObject.h (包含了  NSObject  類定義的頭文件)中包含  NSCopyingNSMutableCopying  和  NSCoding  協議。當某個類採用了這些協議後,它便得到了對象拷貝和對象編碼的基本對象行爲。模型類(封裝了應用數據並管理這些數據的實例的類)常常採用對象拷貝和對象編碼協議。 app

NSObject  類和相關協議定義了建立對象、瀏覽繼承鏈、查閱對象的特徵和功能、比較對象、拷貝對象和把對象進行編碼等的一系列方法。本文接下來主要講述的就是這類任務的基本要求。 框架

建立對象

一般,建立對象時,要先爲它分配內存,而後將它初始化。雖然這是兩個單獨的步驟,但它們聯繫甚密。有些類能夠經過調用它們的工廠方法來建立對象。 函數

建立對象 – 分配內存和初始化 工具

要爲對象分配內存,對它的類發送一個  alloc  消息就能獲得該類的一個「原始」(未初始化)的實例。當你爲一個對象分配內存時,Objective-C 運行時會在應用的虛擬內存中爲該對象預留足夠大的內存空間。除了分配內存自己以外,這個環節還有另外幾個用途,例如把實例變量所有設爲 0 等。

爲原始實例分配好內存以後,你必須將其初始化。初始化也就是將對象設置爲初始狀態,換句話說,就是讓它的實例變量和屬性爲合理的值,而後再返回這個對象。初始化是爲了保證返回的對象能夠被使用。

你會發如今很多框架中都含有  initializers (初始器)方法,便可以初始化對象的方法。它們的形式大多相似。初始器是實例方法,方法開頭爲  init ,返回一個  id  類型的對象。根對象  NSObject  聲明瞭  init  方法,全部其餘的類都繼承了這個方法。其餘的類固然也能夠聲明本身的初始器,各自要有本身的關鍵字和參數類型。例如,NSURL 類聲明瞭以下初始器:

- (id)initFileURLWithPath:(NSString *)path isDirectory:(BOOL)isDir

當你爲一個對象分配內存並將其初始化的時候,能夠將內存分配方法和初始化方法嵌套起來。若是使用上邊這個初始器的話,能夠寫成這樣:

NSURL *aURL = [[NSURL alloc] initFileURLWithPath:NSTemporaryDirectory() isDir:YES];

做爲一種安全的編程習慣,你能夠檢查返回的對象以驗證對象的建立是否正確。若是建立過程當中發生了意外而致使對象建立失敗,初始器將返回  nil 。雖然 Objective-C 容許對  nil  發送消息而不會產生任何反作用(好比拋出異常),但你的代碼顯然不可能正常工做,由於沒有任何方法可以被調用。你不該該使用  alloc  返回的實例,而要使用初始器返回的實例。

經過調用類的工廠方法來建立對象

經過調用類的工廠方法也能建立一個對象。工廠方法是一種類方法,它可以分配內存、初始化,並返回實例自身。類的工廠方法屬於一種便捷方法,由於它們只需一步就能夠建立對象,而不是上文講過的兩步。它們的形式是這樣的:

+ ( type ) className … (這裏的  類名稱  不包含任何前綴)

Objective-C 框架中的類有些會定義一種工廠方法,這種工廠方法實際上起到了初始器的做用。好比, NSString  就聲明瞭以下兩種方法:

- (id)initWithFormat:(NSString *)format, …;

+ (id)stringWithFormat:(NSString *)format, …;

下邊的例子就是  NSString  工廠方法的一種用法:

NSString *myString = [NSString stringWithFormat:@"Customer: %@", self.record.customerName];

用對象的術語來思考

在運行時,每一個應用都是一組互相協做的對象構成的;這些對象互相之間能夠通訊,以完成應用所需的工做。每一個對象都有本身的角色,至少要對一件事 負責,而且至少鏈接一個其餘對象。(孤立的對象毫無價值。)以下圖所示,對象所組成的網絡中既有框架對象也有應用程序對象。應用程序對象時自定義的子類的 實例,通常繼承自某個父類框架。這些對象組成的網絡通常被成爲對象圖。

app_as_object_network

你須要建立這些鏈接,或者關係,在各個對象之間進行引用。引用的語言形式有不少,其中有實例變量、全局變量,甚至包括(在有限的做用域內)本地 變量。而關係,能夠是一對一關係,也能夠是一對多關係,能夠表示出一系列從屬關係的概念。這些關係就是某個對象對其餘對象進行訪問、溝通或者控制的手段。 被引用的對象天然也就成了消息的接收者。

應用的對象間傳遞的消息是讓應用持續工做的重要因素。比如樂團中的演奏家同樣,應用中的每一個對象都有各自的角色,爲應用的運行履行本身的這部分 職責。有的對象能夠顯示一個橢圓形的界面響應點按動做,有的會管理一些承載各類數據的數據集合,有的則控制整個應用生命週期內的各大事件。但爲了完成它們 各自的任務,它們還必須可以互相交流。每一個對象都要有向同一應用中別的對象發送消息的能力,也要有接收別的對象發來的消息的能力。

有些對象之間緊密成對,即互相之間直接相連,它們互發消息時是很容易的。但還有些非緊密成對的對象,即在對象圖中被分隔開的對象,它們之間要進 行通訊就要另想辦法。Cocoa 和 Cocoa Touch 框架含有許多幫助非緊密成對的對象進行通訊的功能和機制(以下圖所示)。這些機制和技術都創建在一些設計模式之上(咱們會在後面探討),這樣就使得應用更 加高效而且具備超強的可擴展性。

communication_loosely_coupled

管理對象圖,避免內存泄漏

Objective-C 程序裏的對象共同組成一張對象圖:由各個對象和其餘對象的關係(或引用)而造成的網絡。對象之間的引用分爲一對一和一對多(經過對象集合)引用。對象圖十 分重要,由於它是使對象保持生命力的關鍵因素。編譯器會檢查對象圖中引用的強弱,並根據須要保持對象發出或釋放對象消息。

在 C 語言或 Objective-C 語言中,可使用含有全局變量、實例變量或本地變量的結構來構造對象間的引用。這些結構各自都有本身暗含的做用域。好比,本地變量引用的一個對象的做用域 就是聲明它的函數塊所在的位置。一樣重要的是,對象間的引用也是分強弱的。強引用會指示出本身的全部者是誰;指向別人的對象擁有被指向的對象。弱引用則是 指向別人的對象和被指向的對象之間沒有從屬關係。對象的生命週期由它的強引用數量多少決定。只要對象有強引用關係,它就不會被釋放。

Objective-C 裏的引用默認都是強引用。一般來講這很方便,讓編譯器管理對象的運行時生命週期,當你使用對象時它們不會被釋放。可是若是粗心未做全面檢查,對象間的強引 用可能會造成無限循環,以下圖左邊所示。這樣的循環鏈在運行時會致使運行時不會釋聽任何一個對象,它們都有指向本身的強引用。繼而,這樣的死循環就形成了 內存泄露。

strong-ref-cycle-weak-ref

就圖中的對象而言,若是你取消 A 和 B 之間的引用,則 B、C、D、E 構成的子對象圖則「永遠」不會從內存中釋放,由於這些對象每個都有強引用,造成了一個死循環。若是在 E 和 B 之間引入弱引用,就能夠打破強引用死循環了。

爲了修正強引用死循環的問題,精明的程序員會使用弱引用。運行時會持續跟蹤對象的弱引用。一旦對象再也不有強引用,運行時就會從釋放該對象,並將全部指向該對象的引用改成  nil 。對變量來講(全局、實例和本地變量),在對象名前面加上  __weak  限定詞就能夠將其標記爲弱引用。對於屬性來講,可使用  weak  選項。在如下這幾類引用中,你應該使用弱引用:

  • 委託

    @property(weak) id delegate;

    在《設計模式》篇裏,「用設計模式讓應用開發流水線化」教程將向你詳解委託和目標機制。

  • 未被頂級對象引用的插座變量(Outlet)

    @property(weak) IBOutlet NSString *theName;

    插座變量是對象間的一種鏈接(或引用),被歸檔在故事版文件或 nib 文件中,當應用運行並載入故事版或 nib 文件時就會恢復插座變量。故事版或 nib 文件中頂級對象的插座變量通常而言是窗口、視圖、視圖控制器或其餘控制器等,應該爲  強引用 (默認的,或未標記的)。

  • 目標

    (void)setTarget:(id __weak)target

  • 塊對象中指向  self  的引用

    __block typeof(self) tmpSelf = self;
    [self methodThatTakesABlock:^ {
        [tmpSelf doSomething];
    }];

    塊對象會對它捕獲的變量產生強引用。若是你在塊對象裏使用了  self ,則會對  self  產生強引用。因此,若是  self  對塊對象也有強引用(一般都會這樣),就造成了強引用死循環。爲了不死循環,你須要在塊對象的外面建立一個指向  self  的  (或  __block )引用,如上邊的範例所示。

管理對象的可變性

可變對象是指在你建立後可以變動其狀態的對象。通常來講你須要使用屬性或存取方法來進行改變。不可變對象則是建立後便被封裝好,狀態不可改變的對象。在 Objective-C 框架中建立的大部分類的實例都是可變的,但有幾種是不可變的。不可變對象具備以下優勢:

  • 在使用不可變對象時,不用擔憂它的值會發生意外變化。
  • 對於許多類型的對象而言,不可變對象可以提高應用程序的性能。

在 Objective-C 框架中,不可變類的實例一般是封裝起來的離散值或緩衝區值的集合,好比數組和字符串。這些類一般帶有一個可變的衍生類,類名裏多出「 Mutable 」(可變)一詞。好比有一個  NSString  類(不可變)和  NSMutableString  類。須要注意的是,對於  NSNumber  或  NSDate  等封裝了離散值的不可變對象,就不必存在可變的衍生類了。

若是你須要常常改變對象的內容,那麼就使用可變對象,而不使用不可變對象。若是你從框架中接收到的對象是個不可變對象,請遵守返回的類型來行事,不要嘗試改變對象的內容。

建立並使用值對象

值對象是指封裝了(C 語言類型的)基本數據類型值的對象,並提供一系列與該值有關的功能。值對象在對象表中表明的是標量類型。Foundation 框架爲你提供了下列類,用來生成字符串、二進制數據、日期和時間、數字等值對象:

  • NSString  和  NSMutableString
  • NSData  和  NSMutableData
  • NSDate
  • NSNumber
  • NSValue

值對象在 Objective-C 編程中十分重要,由於應用會把這些對象看成方法、函數的參數和返回值進行調用。經過傳遞值對象,框架裏的各個部分甚至不一樣的框架之間便可以交換數據。由於 值對象表明的是標量值,所以你能夠在集合或者其餘須要用到對象的地方使用它們。值對象除了有普通數據類型的相同特徵和做爲編程的必要成分以外,還有更大的 優點:你可以經過更加有效並且十分優雅的方式對這些封裝起來的值進行操做。就拿  NSString  類來舉例,它有搜索並替換字符串的方法,有寫入字符串到文件或(更經常使用)到 URL 的方法,還有構建文件系統路徑的方法等。

在有些場合中,你可能以爲使用基本數據類型更加有效和直接,例如  int (整數型)、 float (浮點型)等等。舉個具體的例子就是在計算某個值的時候。因此  NSNumber  和  NSValue  對象不多被看成框架中方法的參數和返回值。然而,須要注意到許多框架會聲明本身的數值數據類型,並把這些數據類型看成參數和返回值進行傳遞和調用,好比  NSInteger  和  CGFloat 。你須要在合適的場合使用這些框架定義的數據類型,這樣可以幫助你把代碼提煉出來,遠離底層平臺。

使用值對象的基本方法

建立值對象的基本模式是:爲你的代碼或框架代碼利用基本數據類型值建立一個值對象(可能稍後就將其做爲方法的參數傳遞出去)。在你的代碼中,你稍後就會訪問對象中封裝的數據了。用  NSNumber  類來舉例再合適不過了:

int n = 5; // 基本數據類型的賦值
NSNumber *numberObject = [NSNumber numberWithInt:n]; // 利用基本數據類型建立一個值對象
int y = [numberObject intValue]; // 從值對象中得到封裝後的數值(y == n)

多數「值」類會聲明一個初始器以及用來建立實例的工廠方法。有些類——好比  NSString  和  NSData  不只提供了初始器,還有利用存儲在本地、遠程文件甚至內存中的數據來建立實例的工廠方法。這些類還提供了一些補充方法,能夠將字符串和二進制數據寫入某個文件或 URL 制定的位置中。下邊的範例代碼演示了  initWithContentsOfURL:  方法利用一個 URL 對象中制定的文件的內容建立了一個  NSData  對象;在使用完數據以後,代碼將數據對象寫回文件系統中:

NSURL *theURL = // 利用字符串路徑建立文件 URL 的代碼…
NSData *theData = [[NSData alloc] initWithContentsOfURL:theURL];
// 使用獲得的數據…
[theData writeToURL:theURL atomically:YES];

大多數值類除了可以建立值對象並讓你訪問封裝好的值之外,還提供一系列簡單的操做好比比較對象等。

字符串

做爲 C 語言的超集,Objective-C 關於字符串的用法和 C 語言同樣。換句話說,單個字母用單括號包裹,字符串用雙括號。不過,Objective-C 框架通常而言不會使用 C 風格的字符串,而是使用  NSString  對象。

在《你的第一個 iOS 應用》教程中,在編寫  HelloWorld  應用時你曾建立了一個格式化的字符串:

NSString *greeting = [[NSString alloc] initWithFormat:@」Hello, %@!」, nameString];

NSString  類爲字符串提供了一個對象包裹,所以自帶有不定長字符串存儲的內存管理功能、支持衆多字符編碼(尤爲是 Unicode 編碼)、以及  printf  風格的格式化語法。由於你會常常用到字符串,所以 Objective-C 提供了利用常量建立  NSString  對象的快捷形式。要使用這種快捷形式,只需在常規的雙引號包裹的字符串前邊加上  @  符號,像下面的範例中這樣:

// 建立字符串「My String」並帶上一個換行符
NSString *myString = @」My String\n」;
// 建立一個格式化字符串「1 String」
NSString *anotherString = [NSString stringWithFormat:@"%d %@", 1, @"String"];
// 利用一個 C 語言字符串建立 Objective-C 字符串
NSString *fromCString = [NSString stringWithCString:"A C string" encoding:NSASCIIStringEncoding];

時間和日期

NSDate  對象和其餘的值對象不一樣,由於它在根本上是時間而不是基本數據類型。日期對象利用參考時間,按秒封裝了一個間隔值。參考時間就是 GMT 2001 年 1 月 1 日的第一個實例。

光是  NSDate  的實例自身可能用處還不是很大。它確實能表明某個時刻,但沒有日曆、時區和個別地區的時間約定等這些上下文,並沒有太大意義。幸虧 Foundation 類提供了這些概念的實體:

  • NSCalendar  和  NSDateComponents :你能夠將日期和日曆聯繫起來,包括由此引伸出的時間單位例如年、月、小時、一週中的某一天等。你還能夠進行日期的計算。
  • NSTimeZone :當日期和時間必須反映出某個地區的時區時,你能夠將時區對象和日曆關聯起來。
  • NSLocale :本地化對象,裏面封裝了和時間有關的文化和語言格式的規約。

下面的代碼段展現瞭如何使用  NSDate  對象配合上述這些對象來獲取你須要的信息(本例中,當前時間的打印格式爲小時,分鐘,秒)。請參考代碼段下邊對應的數字項後邊的註釋:

NSDate *now = [NSDate date]; // 1
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; // 2
[calendar setTimeZone:[NSTimeZone systemTimeZone]]; // 3
NSDateComponents *dc = [calendar components:(NSHourCalendarUnit|NSMinuteCalendarUnit|
    NSSecondCalendarUnit) fromDate:now];  // 4
NSLog(@」The time is %d:%d:%d」, [dc hour], [dc minute], [dc second]); // 5

  1. 建立一個表示當前時間的日期對象。
  2. 建立一個表示公曆的對象。
  3. 用表明系統偏好設置中設定的時區的對象,設置日曆對象的時區。
  4. 調用日曆對象的  components:fromDate:  方法,將第一步裏建立的日期對象做爲參數傳遞。調用此方法後會返回一個包含了時間對象的小時、分鐘和秒元素的對象。
  5. 在控制檯打印出當前的時、分、秒。

雖然這個範例最終將結果打印出來,但更加推薦的使用方式是利用日期格式化器( NSDateFormatter  類的實例)在應用的界面上顯示日期信息。在進行日期計算時必定要選用正確的類和方法;不要對時、分、秒、日等數值單位進行硬編碼。

建立並使用羣體

羣體也是一種對象,它可以以特定方式存儲其餘對象並容許客戶訪問那些對象。你一般會將羣體看成方法和函數的參數進行傳遞,也經常從方法和函數的返回值得到一個羣體。羣體每每包含值對象,但其實它們能夠包含任何類型的對象。大部分羣體對它們所包含的對象會產生強引用。

Foundation 框架種有好幾種羣體,其中三種在 Cocoa 和 Cocoa Touch 編程中極其重要:數組、字典和集合。這些羣體的類一樣分別有不可變與可變的形式。可變羣體可以添加和移除對象,不可變羣體只能含有它們建立時所包含的對 象。全部羣體均可以進行枚舉,也就是輪流檢查所包含的每一個對象。

不一樣類型的羣體會以各自不一樣的方式組織它們所包含的對象:

  • NSArray  和  NSMutableArray :數組是按順序存儲的一系列對象。你能夠經過某個對象的位置序號來找到它(也就是它的索引)。數組中的第一個對象索引爲 0(數字零)。
  • NSDictionary  和  NSMutableDictionary :字典將條目以「鍵值對(Key-Value)」的形式存儲在一塊兒。鍵是惟一標識符,一般是字符串;值就是你想要存儲的對象自己。你能夠經過鍵來直接訪問它對應的對象。
  • NSSet  和  NSMutableSet :集合裏的對象是無序存儲的,而且每一個對象只能出現一次。一般要訪問集合裏的某個或某幾個對象時,你必須使用篩選或對對象進行判斷等方式。

collections

因爲它們的存儲、訪問和性能各有不一樣,在不一樣的場合也就各有利弊。

在數組中以特定順序存儲對象

數組中的對象是按順序存儲的。所以,當順序比較重要時你就能夠選擇數組。舉個例子,許多應用都採用數組來存儲表格視圖中的內容或者菜單中的項目;索引值爲 0 的對象表明第一排,索引值 1 上的對象對應第二排,以此類推。訪問數組中對象的速度比訪問集合的速度稍慢。

NSArray  類有多個初始器和類工廠方法用來建立和初始化數組,其中有幾個尤爲經常使用。你能夠利用一系列對象來建立數組,使用  arrayWithObjects:count:  和  arrayWithObjects:  方法(及其對應的初始器)便可。前邊一個方法的第二個參數能夠用來限制第一個參數中的對象個數;後面的方法中你可使用  nil  來停止一系列用半角逗號分隔的對象。

// 建立一個含有字符串對象的靜態數組
NSString *objs[3] = {@」One」, @」Two」, @」Three」};
// 用該靜態對象建立一個新數組對象
NSArray *arrayOne = [NSArray arrayWithObjects:&(*objs) count:3];
// 建立一個用 nil 結尾的對象列表的數組
NSArray *arrayTwo = [[NSArray alloc] initWithObjects:@」One」, @」Two」, @」Three」, nil];

在建立可變數組時,你可使用  arrayWithCapacity: (或  initWithCapacity: )方法來建立此數組。容量參數只是做爲期待數組大小的預設值,可以讓數組在運行時更加高效。也就是說,數組的實際大小能夠超過所指定的容量。

通常狀況下,要經過索引位置(從 0 起始)訪問數組中的對象時須要調用  objectAtIndex:  這個方法:

NSString *theString = [arrayTwo objectAtIndex:1]; // 返回數組中的第二個對象

NSArray  還有其餘方法,你能夠訪問數組中的對象,也能夠訪問它們的索引。好比  lastObjectfirstObjectCommonWithArray:  和  indexOfObjectPassingTest:  方法。

數組的另外一個重要功能是對所包含的每一個對象均進行操做,這個過程叫作枚舉。你一般會枚舉某個數組,以此判斷某個或某些對象是否符合某個值或者條 件,若是條件成立則能夠進一步進行操做。共有三種枚舉方式可供選用:快速枚舉,塊對象枚舉,或者使用 NSEnumerator 對象。快速枚舉正如其名 稱所示,通常而言在獲取數組中的對象時比其餘枚舉方式更快。快速枚舉有其特定的語法:

for   (type variable   in   array)   { /* 規定   variable ,並執行所需的操做 */ }

好比此例:

NSArray *myArray = // 獲取數組
for (NSString *cityName in myArray) {
    if ([cityName isEqualToString:@"Cupertino"]) {
        NSLog(@」We’re near the mothership!」);
        break;
    }
}

有幾種  NSArray  方法是經過塊對象進行枚舉的,最簡單的一個是  enumerateObjectsUsingBlock: 。塊對象有三個參數:當前對象,它的索引值,以及一個布爾值,若是它爲  YES  則枚舉結束。塊對象中的代碼效果和花括號裏的快速枚舉效果徹底同樣:

NSArray *myArray = // 獲取數組
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if ([(NSString *)obj isEqualToString:@"Cupertino"]) {
        NSLog(@」We’re near the mothership!」);
        *stop = YES;
    }
}];

NSArray  還有數組排序、搜索、對數組中每一個對象起做用等的方法。

若要往可變數組中添加對象,則要調用  addObject:  方法;新增的對象會被放置在數組末尾。你也可使用  insertObject:atIndex:  將對象放在數組中的某特定位置。經過調用  removeObject:  或者  removeObjectAtIndex:  方法就能夠將對象從數組中移除了。

用字典存儲鍵值對

利用字典能夠將對象以鍵值對的形式存儲在羣體中,鍵值對是指一個標識符(鍵)與一個對象(值)組成的對子。字典是無序羣體,由於鍵值對能夠以任何順序存儲。雖然鍵能夠是任意形式,但最好是可以描述值的字符串,好比  NSFileModificationDate  或  UIApplicationStatusBarFrameUserInfoKey (都是字符串常量)。當它們是公有鍵時,用字典在任意類型的對象之間傳遞信息再好不過了。

經過它的初始器和類工廠方法, NSDictionary  類有許多建立字典的方式,但其中兩個是最爲經常使用的: dictionaryWithObjects:forKeys:  和  dictionaryWithObjectsAndKeys: (或者它們對應的初始器)。前一個方法中你須要傳入一個對象數組和鍵數組;鍵和值要在位置上一一對應。後面一個方法中你須要指定第一個對象值和它的鍵、第二個對象值和它的鍵、第三個、第四個,以此類推;用  nil  即可以結束這個對象系列。

// 首先建立一個鍵的數組以及一個值的補充數組
NSArray *keyArray = [NSArray arrayWithObjects:@"IssueDate", @"IssueName", @"IssueIcon", nil];
NSArray *valueArray = [NSArray arrayWithObjects:[NSDate date], @」Numerology Today」,
    self.currentIssueIcon, nil];
// 建立字典,將鍵數組和值數組傳入
NSDictionary *dictionaryOne = [NSDictionary dictionaryWithObjects:valueArray forKeys:keyArray];
// 用值、鍵輪流的方式建立數組,用 nil 來結束本字典
NSDictionary *dictionaryTwo = [[NSDictionary alloc] initWithObjectsAndKeys:[NSDate date],
    @」IssueDate」, @」Numerology Today」, @」IssueName」, self.currentIssueIcon, @」IssueIcon」, nil];

要訪問字典中的對象值,須要調用  objectForKey:  方法,並在參數中指定一個鍵。

NSDate *date = [dictionaryTwo objectForKey:@"IssueDate"];

你能夠向可變字典中經過調用  setObject:forKey:  添加條目,也能夠用  removeObjectForKey:  刪除條目,還能夠用  setObject:forKey:  來替換任何給定鍵所對應的值。這些方法運行速度都很快。

用集合存儲無序對象

集合與數組類似也是對象羣體,但集合中的條目是無序存儲的。你沒法經過索引或者鍵來訪問集合裏的對象,而是隨機訪問( anyObject ),經過枚舉羣體或者使用篩選器、測試等方式查找對象。

雖然在 Objective-C 中集合對象不像字典和數組那麼經常使用,它們仍然是某些技術中很是重要的羣體類型。在 Core Data(一種數據管理技術)中,當你聲明一個一對多關係的屬性時,屬性類型就應該是  NSSet  或者  NSOrderedSet 。集合在 UIKit 框架中的原生觸摸事件處理中也是很是重要的,好比:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *theTouch = [touches anyObject];
    // 處理代碼……
}

有序集合是集合定義以外的一個特例。在有序集合中,條目的順序十分重要。測試某個條目是否存在時,有序集合比數組的速度更快。

在運行時檢驗對象能力

內省(Introspection)是 Objective-C 中  NSObject  類的一個強大而實用的特性,可讓你在運行時獲知關於對象的一些信息。這樣,你就能避免一些錯誤,好比將消息發送給一個不認識它的對象,或者覺得某個對象繼承自另外一個對象,實際上卻不是。

在運行時,對象能夠傳達關於它本身的三種重要類型的信息。

  • 它是不是某個類或子類的實例
  • 它是否能響應某條消息
  • 它是否遵照某個協議

探究對象是不是某個類或其子類的實例

這樣作的方式是對對象調用  isKindOfClass:  方法:

static int sum = 0;
for (id item in myArray) {
    if ([item isKindOfClass:[NSNumber class]]) {
        int i = (int)[item intValue];
        sum += i;
    }
}

isKindOfClass:  方法須要一個  Class  類型的對象做爲參數;要得到這個對象,在類符號上調用  class  方法即可。檢查此方法返回的布爾值並進行下一步操做。

NSObject  還聲明瞭其餘用來探究對象繼承信息的方法。好比  isMemberOfClass:  方法會告訴你對象是不是某個指定類的實例,而  isKindOfClass:  會告訴你對象是不是某個類或其子類的成員。

探究對象是否可以響應某個消息

這樣作的方法是對對象調用  respondsToSelector:  方法:

if ([item respondsToSelector:@selector(setState:)]) {
    [item setState:[self.arcView.font isBold] ? NSOnState : NSOffState];
}

respondsToSelector:  方法須要一個選擇器做爲參數。選擇器是 Objective-C 的一個數據類型,能夠在運行時標識某個方法;利用  @selector  編譯器指令能夠指定該選擇器。在你的代碼中,檢查該方法返回的布爾值並進行下一步操做。

爲了標識要發送給對象的消息,一般是調用  respondsToSelector:  方法,這比檢測類的類型要更有用。好比,某個類的最新版本中可能實現了一箇舊版本中不存在的方法。

探究對象是否遵照某個協議

這樣作的方法是對對象調用  conformsToProtocol:  方法:

- (void) setDelegate:(id __weak) obj {
    NSParameterAssert([obj conformsToProtocol:
        @protocol(SubviewTableViewControllerDataSourceProtocol)]);
    delegate = obj;
}

conformsToProtocol:  方法須要協議的運行時標識符做爲參數;利用  @protocol  編譯器指令能夠指定此標識符。接下來檢查該方法返回的布爾值並進行下一步操做。

比較對象

利用  isEqual:  方法能夠將兩個對象進行比較。接收到此消息的對象將和做爲參數傳入的對象進行比較;若是它們相同則此方法返回  YES 。範例:

BOOL objectsAreEqual = [obj1 isEqual:obj2];
if (objectsAreEqual) {
    // 執行某些操做…
}

注意,對象的相等並非對象的同一。對象的同一性要用  ==  來檢測兩個變量是否指向同一個實例。

那麼當你比較兩個對象時,到底是在比較什麼內容呢?這要視具體的類而定了。根類  NSObject  使用指針相等性來做爲比較的基本點。其下任何層級的子類都可將父類比較的基本點的實現按照特定類的條件進行重寫,好比對象狀態。舉例來講,假設有個 Person(人)對象和另外一個 Person 對象,若是它們的姓、名、生日屬性都相同,那麼就斷定它們相等。

Foundation 框架的值和羣體對象以  isEqualToType:  的形式聲明比較方法,這裏的  Type  是去掉「NS」前綴的類名稱,例如  isEqualToString:  和  isEqualToDictionary: 。比較方法能夠用來肯定傳入的對象是否適合於某個給定的類型,以便接下來進行其餘形式的比較。

拷貝對象

要拷貝某個對象,對其調用  copy  方法便可:

NSArray *myArray = [yourArray copy];

做爲被拷貝的對象,接收消息的對象的類必須遵照  NSCopying  協議。要讓對象可以被拷貝,你必須採用這個協議的  copy  方法並實現它。

當你須要使用程序另外一個地方的對象並想要徹底保持對象的各項狀態的話,就須要拷貝這個對象。

拷貝行爲是根據類的不一樣而各自區別的,並且還依賴於個別實例的特性。大多數類實現了深拷貝,它會複製實例中的全部實例變量和屬性;有些類則實現淺拷貝,它只會複製實例變量和屬性的引用。

擁有可變與不可變兩種變體的類還會聲明一個  mutableCopy  方法,用來建立對象的可變拷貝。好比,若是對一個  NSString  對象調用  mutableCopy  方法,你會獲得一個  NSMutableString  實例。

相關文章
相關標籤/搜索