Cocoa 架構 For iOS(二)對象的分配初始化、內省、單例

來源:互聯網
上載者:User
文章目錄
  • 初始化的問題
  • 實現初始化方法

接上一篇  Cocoa 架構總結For iOS(一)  ,繼續總結Cocoa對象

1、Cocoa對象的建立我們都知道建立一個對象有兩步:alloc 和 init(對象分配和初始化),兩步缺一不可。初始化一般都是緊接著對象分配的後面進行,但是這兩個操作的作用是完全不同的。指派至:就是Cocoa從應用程式的虛擬記憶體中為對象分配一塊記憶體。Cocoa會根據對象的執行個體變數(類型和變數的排列順序)計算記憶體大小並分配記憶體。為了分配記憶體,你需要向 類對象類對象上篇詳細講了它的由來和作用)發送alloc 或者allocWithZone:發送訊息。訊息返回一個未初始化的類執行個體。那發送分配訊息除了分配記憶體外,還做了其他的一些很重要的工作:
  • 對象的保持(retain)數設定為1.
  • 分配的對象的isa指標指向類對象。
  • 把對象所有的執行個體變數初始化為0.也可以理解成0的等價類別型:nil  NULL
這樣所有的對象都有了isa指標,而且指向它們對應的類對象,這樣對象就可以找到它運行時的資訊。比如對象在繼承層次機構上所在的位置(哪個是父類,哪個是子類等資訊),它實現的協議,還有能響應什麼訊息。即便如此,alloc之後的對象還不是一個可用的對象,對象必須初始化。1.1初始化對象初始化過程就是把對象的執行個體變數設定成有效合理的值,或者說你想要的資料。如果你的類沒有實現初始化方法,它會調用父類的。 初始化方法的形式初始化方法是執行個體方法,返回的是id類型的對象。初始化方法是講究形式的,不能亂寫。方法你可以有參數,多個也行,但是必須是init開頭,比如:- (id)initWithArray:(NSArray *)array; (from NSSet)參數形式:WithType: 初始化的問題初始化也有問題?啥問題!?有時候初始化返回的並不是一個新的對象。什麼時候呢? 比如:我們熟悉的單例模式的時候。還有保持對象某個屬性唯一的時候。賬戶類的id唯一性,如果初始化一個id是已存在的id,那就要返回已存在id對應的賬戶對象。這時候我們需要:
  • 釋放剛剛分配的對象(是不是感覺很浪費,剛分配了又要釋放,都沒用了呢,沒辦法的事情啊)
  • 返回已存在的賬戶對象
有時候你初始化對象失敗了,也需要有一些操作。怎麼會失敗呢?比如:initFromFile: 這個初始化方法,它是要從一個檔案初始化,萬一這個檔案不存在,那就是初始化失敗,初始化失敗了怎麼辦:
  • 釋放剛剛分配的對象
  • 返回nil
對象不能重複初始化,不然會產生NSInvalidArgumentException異常。 實現初始化方法自訂類可能就需要自己寫初始化的方法 了,可以有一個或多個初始化方法,看你設計的類的需要。不過實現初始化方法需要遵循以下步驟:
  1. 先要調用父類的初始化方法
  2. 檢查父類初始化返回的對象,如果是nil則初始化失敗,也返回nil
  3. 在初始化執行個體變數時,如果它們是其他對象的引用,必要時要進行retain和copy
  4. 如果返回一個已存在的對象,那首先釋放新分配的對象(剛才提到的帳號的類)
  5. 遇到問題初始化不成功(比如初始設定檔案失敗),返回nil
  6. 如果沒有問題,返回self。初始化完成
下面這個例子能說明這幾個步驟,請看:
- (id)initWithAccountID:(NSString *)identifier {    if ( self = [super init] ) {        Account *ac = [accountDictionary objectForKey:identifier];        if (ac) { // object with that ID already exists            [self release];            return [ac retain];        }         if (identifier) {            accountID = [identifier copy]; // accountID is instance variable            [accountDictionary setObject:self forKey:identifier];            return self;        } else {            [self release];            return nil;        }    } else        return nil;}

注意:子類初始化時,必須先調用父類的初始化方法,以保證繼承鏈中父類的執行個體變數得到正確的賦值。

 

解釋繼承鏈的初始化過程:

   1.2 dealloc方法dealloc和init方法是相呼應的。dealloc確保的是對象的執行個體變數和動態分配的記憶體被正確的釋放。和init方法相反,父類的dealloc是在釋放了其他的之後最後調用的。
- (void)dealloc {    [accountDictionary release];    if ( mallocdChunk != NULL )        free(mallocdChunk);    [super dealloc];}

1.3 工廠類方法工廠類方法把指派至和初始化合二為一,返回建立對象,而且還自動釋放。這些方法的形式一般是:+ (type)className...NSDate工廠類方法:

+ (id)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;+ (id)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)secs;+ (id)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;

測試下第一個代碼:

 

    NSDate *now = [NSDate dateWithTimeIntervalSinceNow: 0];    NSLog(@"now:%@",now);

 

列印出來now:2012-10-23 06:39:25 +0000

引目前時間為基準,0是目前時間,+0000表示是時區,咱們是8時區,+8是14:39。如果參數是24*60*60是明天的時間,如果是負數那就是昨天的時間。

NSData提供下面的Factory 方法:

+ (id)dataWithBytes:(const void *)bytes length:(unsigned)length;+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length;+ (id)dataWithBytesNoCopy:(void *)bytes length:(unsigned)length        freeWhenDone:(BOOL)b;+ (id)dataWithContentsOfFile:(NSString *)path;+ (id)dataWithContentsOfURL:(NSURL *)url;+ (id)dataWithContentsOfMappedFile:(NSString *)path;
舉個 + (id)

dataWithContentsOfURL的例子,下載圖片,毫無壓力。

        NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];        NSData * data = [NSData dataWithContentsOfURL:url];        UIImage *image = [[UIImage alloc]initWithData:data];
2、運行時內省的能力內省(Introspection)是物件導向語言和環境的重要特性,Objective-C和Cocoa在這方面做的很好。內省是對象自己檢查自己做為運行時對象詳細資料的一種能力。這些詳細資料包括對象在繼承樹上的位置,對象是否遵循特定的協議,以及是否可以響應特定的訊息。NSObject協議和類定義了很多內省方法,用於查詢運行時資訊,以便根據對象的特徵進行識別。

靈活的使用內省能力可以讓你的程式更穩定強大。內省可以避免錯誤地進行訊息派發、對象相等的錯誤判斷等問題。下面介紹內省的一些實用方法:

2.1 定位繼承關係NSObject協議聲明了幾個方法,用於確定對象在類層次中的位置。class返回類的Class對象。superclass返回父類的Class對象。看下面例子:
while ( id anObject = [objectEnumerator nextObject] ) {    if ( [self class] == [anObject superclass] ) {        // do something appropriate...    }}

返回的兩個Class對象看是否相等。
檢查類對象的從屬關係:isKindOfClass:判斷是否是這個類的或這個類的子類的執行個體。isMemberOfClass: 這個更嚴格些,判斷是否是這個類的執行個體。例子:

if ([item isKindOfClass:[NSData class]]) {    const unsigned char *bytes = [item bytes];    unsigned int length = [item length];    // ...}
2.2 判斷方法的實現或者是否遵循某個協議NSObject還有兩個功能更加強大的內省方法,即respondsToSelector:和conformsToProtocol:。兩個是執行個體方法。respondsToSelector判讀對象是否實現某個的方法,conformsToProtocol判斷是否遵循指定的正式協議(正是協議的意思是實現該協議的所有方法)。所有繼承NSObject的類都有有這兩個方法。respondsToSelector例子:
- (void)doCommandBySelector:(SEL)aSelector {    if ([self respondsToSelector:aSelector]) {        [self performSelector:aSelector withObject:nil];    } else {        [_client doCommandBySelector:aSelector];    }}

 

2.3 對象的比較

hash和isEqual:方法都在NSObject協議中聲明,且彼此關係緊密。實現hash方法會返回一個整型數。兩個對象相等意味著它們有相同的雜湊值。如果您的對象可能被包含在象NSSet這樣的集合中,則需要定義hash方法,並確保該方法在兩個對象相等的時候返回相同的雜湊值。不過NSObject類中預設的isEqual實現只是簡單地檢查指標是否相等。

isEqual方法例子:

- (void)saveDefaults {    NSDictionary *prefs = [self preferences];    if (![origValues isEqual:prefs])         [Preferences savePreferencesToDefaults:prefs];}

如果子類增加了執行個體變數,比較子類需要對子類的執行個體變數也做比較才能確定對象是否相等時,需要重載isEqual方法:

- (BOOL)isEqual:(id)other {    if (other == self)         return YES;    if (!other || ![other isKindOfClass:[self class]])         return NO;    return [self isEqualToWidget:other];} - (BOOL)isEqualToWidget:(MyWidget *)aWidget {    if (self == aWidget)         return YES;    if (![(id)[self name] isEqual:[aWidget name]])        return NO;    if (![[self data] isEqualToData:[aWidget data]])        return NO;    return YES;}
3、對象可變性(mutable)3.1 為什麼要有可變與不可變對象建立對象的時候,選可變的對象還是選不可變的對象呢?怎麼決定呢。先看看為什麼要有可變與不可變這兩種對象的存在。可變的對象的類前面都有 Mutable的關鍵字,這些類有:
  • NSMutableArray
  • NSMutableDictionary
  • NSMutableSet
  • NSMutableIndexSet
  • NSMutableCharacterSet
  • NSMutableData
  • NSMutableString
  • NSMutableAttributedString
  • NSMutableURLRequest
它們都是對應的不可變類的子類。如果對象都是可變的,那在某些情境中是很不安全和不可靠的。比如你的某個對象當做參數傳給了某個方法,你不希望你的對象被改變。這時這個方法卻你的變數改變了,這是你不想要的結果。而在另外一些情境卻相反。OK,為了對應不同的情境,對象就必須有可變與不可變之分了。3.2 什麼時候用可變對象當需要在對象建立之後頻繁或不斷地對其內容進行修改時,請使用可變對象
有些時候,用一個不可變對象取代另一個可能更好。比如,大多數保留字元串的執行個體變數都應該被賦值為一個不可變的NSString對象,而這些對象則用“setter”方法來進行替換。
依靠傳回型別來進行可變性提示。
如果你不能確定一個對象是可變的,則將它當成不可變的處理。
4、建立單例建立單例的步驟:
  • 聲明一個單例對象的靜態執行個體,並初始化為nil。
  • 在該類的類Factory 方法(名稱類似於“sharedInstance”或“sharedManager”)中產生該類的一個執行個體,但僅當靜態執行個體為nil的時候。
  • 重載allocWithZone:方法,確保當使用者試圖直接(而不是通過類Factory 方法)分配或初始化類的執行個體時,不會分配出另一個對象。
  • 實現基本協議方法:copyWithZone:、release、retain、retainCount、和autorelease ,以保證單例的狀態。
實現單例的代碼例子:
static MyGizmoClass *sharedGizmoManager = nil; + (MyGizmoClass*)sharedManager{    @synchronized(self) {        if (sharedGizmoManager == nil) {            [[self alloc] init]; // assignment not done here        }    }    return sharedGizmoManager;} + (id)allocWithZone:(NSZone *)zone{    @synchronized(self) {        if (sharedGizmoManager == nil) {            sharedGizmoManager = [super allocWithZone:zone];            return sharedGizmoManager;  // assignment and return on first allocation        }    }    return nil; //on subsequent allocation attempts return nil} - (id)copyWithZone:(NSZone *)zone{    return self;} - (id)retain{    return self;} - (unsigned)retainCount{    return UINT_MAX;  //denotes an object that cannot be released} - (void)release{    //do nothing} - (id)autorelease{    return self;}

參考:

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW59

容芳志 (http://blog.csdn.net/totogo2010)

本文遵循“署名-非商業用途-保持一致”創作公用協議

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.