編寫高質(zhì)量iOS的52個(gè)有效方法學(xué)習(xí)筆記(6-12)

C語言是面向過程的語言,Objective-C語言是面向?qū)ο蟮恼Z言,“對(duì)象”是基本構(gòu)造單元,開發(fā)者可以通過對(duì)象來存儲(chǔ)并傳遞數(shù)據(jù)。對(duì)象之間傳遞數(shù)據(jù)并執(zhí)行任務(wù)的過程稱為“消息傳遞”。
當(dāng)應(yīng)用程序運(yùn)行起來以后,為其提供相關(guān)技術(shù)支持的代碼叫做“Objective-C 運(yùn)行期環(huán)境”(Objective-C runtime),它提供了一些使得對(duì)象之間傳遞消息的重要函數(shù),并且包含創(chuàng)建類實(shí)例的全部邏輯。

6.理解“屬性”這一概念

“屬性”(property)是Objective-C的一項(xiàng)特性,用于封裝對(duì)象中的數(shù)據(jù)。

 @property (nonatomic,copy,readonly) NSString *sex;

這種寫法包含了getter和setter方法,使用點(diǎn)語法可以來調(diào)用getter和setter方法

self.sex = @"男"; //same as:
[self setSex:@"男"];
    
NSString *sexStr = self.sex; //same as:
NSString *sexStr = [self sex];

屬性的幾個(gè)特性:原子性、讀寫性、語義性

對(duì)于atomic的屬性,系統(tǒng)生成的 getter/setter 會(huì)保證 get、set 操作的完整性,不受其他線程影響。比如,線程 A 的 getter 方法運(yùn)行到一半,線程 B 調(diào)用了 setter:那么線程 A 的 getter 還是能得到一個(gè)完好無損的對(duì)象。
而nonatomic就沒有這個(gè)保證了。所以,nonatomic的速度要比atomic快。
  • Atomic

    是默認(rèn)的
    會(huì)保證 CPU 能在別的線程來訪問這個(gè)屬性之前,先執(zhí) 行完當(dāng)前流程
    速度不快,因?yàn)橐WC操作整體完成

  • Non-Atomic

    不是默認(rèn)的
    更快
    線程不安全
    如有兩個(gè)線程訪問同一個(gè)屬性,會(huì)出現(xiàn)無法預(yù)料的結(jié)果

讀寫特性:readwrite擁有g(shù)etter(獲取方法)與setter(設(shè)置方法),若該屬性由@synthesize實(shí)現(xiàn),則編譯器會(huì)自動(dòng)生成這兩種方法,@synthesize在.m中聲明,系統(tǒng)默認(rèn)是有的,可以不寫。
readonly(只讀)僅有獲取方法

語義特性

1,當(dāng)把語義特性聲明為assign時(shí),setter和getter時(shí)方法內(nèi)部實(shí)現(xiàn)

- (void)setName:(NSString *)name{

_name = name;

}

- (NSString *)name{

return _name;

}
2,當(dāng)把語義特性聲明為retain時(shí),setter和getter方法內(nèi)部實(shí)現(xiàn)

- (void)setName:(NSString *)name{

    if (_name != name) {

    [ _name release];

    _name = [name retain];

    }

}
- (NSString *)name{

    return [[ _name retain] autorelease];

}
3,當(dāng)把語義特性聲明為copy時(shí),setter和getter方法內(nèi)部實(shí)現(xiàn)

- (void)setName:(NSString *)name{

    if (_name != name) {

    [ _name release];

    _name = [name copy];

    }

}

- (NSString *)name{

    return [[ _name retain] autorelease];

}
  • 可以用@property語法來定義對(duì)象中所封裝的數(shù)據(jù)
  • 通過“特性”來指定存儲(chǔ)數(shù)據(jù)所需的正確語義
  • 在設(shè)置屬性所對(duì)應(yīng)的實(shí)例變量時(shí),一定要遵循屬性所聲明的語義
  • 開發(fā)iOS程序時(shí)應(yīng)該使用nonatomic屬性,因?yàn)閍tomic屬性會(huì)嚴(yán)重影響性能

7、在對(duì)象內(nèi)部盡量直接訪問實(shí)例變量

在對(duì)象之外訪問實(shí)例變量時(shí),總是應(yīng)該通過屬性來做,然而在對(duì)象內(nèi)部訪問實(shí)例變量時(shí)的建議是:在寫入實(shí)例變量時(shí)通過其“設(shè)置方法”來做,而在讀取實(shí)例變量時(shí),則直接訪問之。之所以要通過“設(shè)置方法”來寫入實(shí)例變量,其首要原因在于,這樣做能夠確保相關(guān)屬性的“語義特性”得以貫徹。
需要注意的兩種情況是:1、在初始化方法中應(yīng)該直接訪問實(shí)例變量,因?yàn)樽宇惪赡軙?huì)“覆寫”(overrede)設(shè)置方法。2、懶加載。必須通過“獲取方法”來訪問屬性,否則,實(shí)例變量就永遠(yuǎn)不會(huì)初始化。

以上獲取方法指的就是點(diǎn)語法。

  • 在對(duì)象內(nèi)部讀取數(shù)據(jù)時(shí),應(yīng)該直接通過實(shí)例變量來讀,而寫入數(shù)據(jù)時(shí),則應(yīng)通過屬性來寫。
  • 在初始化方法以及dealloc方法中,總是應(yīng)該直接通過實(shí)例變量來讀寫數(shù)據(jù)
  • 使用懶加載的話必須用點(diǎn)語法操作。

8、理解“對(duì)象等同性”這一概念

- (BOOL)isEqual:(id)object {
  if ([self class] == [object class]) {
      return [self isEqualToPerson:(Person *)object];
  }else{
      return [super isEqual:object];
  }
}

- (BOOL)isEqualToPerson:(Person *)person {
  if (self == object) {
      return YES;
  }
  if (![_firstName isEqualToString:person.firstName]) {
      return NO;
  }
  if (![_lastName isEqualToString:person.lastName]) {
      return NO;
  }
  if (_age != person.age ) {
      return NO;
  }
  return YES;
}
  • 若想檢測對(duì)象的等同性,請?zhí)峁癷sEqual:”與hash 方法。
  • 相同的對(duì)象必須具有相同的哈希碼,但是兩個(gè)哈希碼相同的對(duì)象卻未必相同。
  • 不要盲目的逐個(gè)檢測每條屬性,而是應(yīng)該依照具體需求來指定檢測方案。
  • 編寫hash方法時(shí),應(yīng)該使用計(jì)算速度快而且哈希碼碰撞幾率低的算法。

9、以“類族模式”隱藏實(shí)現(xiàn)細(xì)節(jié)

“類族”是一種很有用的模式,可以隱藏“抽象基類”背后的實(shí)現(xiàn)細(xì)節(jié)。類方法,(工廠模式)

  • 類族模式可以把實(shí)現(xiàn)細(xì)節(jié)隱藏在一套簡單的公共接口后面。
  • 系統(tǒng)框架中經(jīng)常使用類族。
  • 從類族的公共抽象基類中繼承子類時(shí)要當(dāng)心,若有開發(fā)文檔,則應(yīng)首先閱讀。

10、在既有類中使用關(guān)聯(lián)對(duì)象存放自定義數(shù)據(jù)

有時(shí)需要在對(duì)象中存放相關(guān)信息、這時(shí)我們通常會(huì)從對(duì)象所屬的類中繼承一個(gè)子類,然后改用這個(gè)子類對(duì)象。然而并非所有情況下都能這么做,有時(shí)候類的實(shí)例可能是由某種機(jī)制創(chuàng)建的,而開發(fā)者無法令這種機(jī)制創(chuàng)建出自己的子類實(shí)例。Object-C中有一項(xiàng)強(qiáng)大的特性可以解決這個(gè)問題,這就是“關(guān)聯(lián)對(duì)象”(Associated Object)

關(guān)聯(lián)類型 等效的@property
OBJC_ASSOCIATION_ASSIGN assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC nonatomic,retain
OBJC_ASSOCIATION_COPY_NONATOMIC nonatomic,copy
OBJC_ASSOCIATION_RETAIN retain
OBJC_ASSOCIATION_COPY copy

下列方法可以管理關(guān)聯(lián)對(duì)象:

void objc_setAssociatedObject (id object,void *key,id value,objc_AssociationPolicy policy)
此方法以給定的鍵和策略為某對(duì)象設(shè)置關(guān)聯(lián)對(duì)象值。

id objc_getAssociatedObjects(id object,void *key)
此方法根據(jù)給定的鍵從某對(duì)象中獲取相應(yīng)的關(guān)聯(lián)對(duì)象值。

void objc_removeAssociatedObjects(id object)
此方法移除指定對(duì)象的全部關(guān)聯(lián)對(duì)象。

關(guān)聯(lián)對(duì)象用法舉例:
ios 開發(fā)時(shí)經(jīng)常用到UIAlertView類,該類提供了一種標(biāo)準(zhǔn)試圖,用來提示信息。但是創(chuàng)建的代碼和處理按鈕動(dòng)作的代碼是分開的,如:

- (void)viewDidLoad {
UIAlertView *view = [[UIAlertView alloc] initWithTitle:@"標(biāo)題" message:@"消息" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"1", nil];
    [view show];
}

#pragma mark --UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
     if (buttonIndex == 0) {
            NSLog(@"0");
        }else{
            NSLog(@"1");
        }
}

如果想在同一個(gè)類里處理多個(gè)警告信息視圖,那么代碼就會(huì)變得更加復(fù)雜,我們必須在delegate方法中檢查傳入的alertview參數(shù),并據(jù)此選用相應(yīng)的邏輯。要是能在創(chuàng)建視圖的時(shí)候就直接把處理每個(gè)按鈕的邏輯都寫好,就簡單多了。這可以通過關(guān)聯(lián)對(duì)象來做。創(chuàng)建完視圖后,設(shè)定一個(gè)與之關(guān)聯(lián)的“塊(block)”等到執(zhí)行delegate方法時(shí)再將其讀出來,如下:

#import <objc/runtime.h>

static void *key = @"key";
- (void)viewDidLoad {
UIAlertView *view = [[UIAlertView alloc] initWithTitle:@"標(biāo)題" message:@"消息" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"1",@"2", nil];

void (^block)(NSInteger) = ^(NSInteger buttonIndex){
        if (buttonIndex == 0) {
            NSLog(@"0");
        }else{
            NSLog(@"1");
        }
 };
    
 objc_setAssociatedObject(view, key, block, OBJC_ASSOCIATION_COPY);
 [view show];
}

#pragma mark --UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    void (^block)(NSInteger) = objc_getAssociatedObject(alertView, key);
    block(buttonIndex);
}

以這種寫法,創(chuàng)建和處理的代碼放在一起,比較方便查閱。但是,采用該方法時(shí)需要注意:塊可能要捕獲某些變量,也許會(huì)造成“循環(huán)引用”。

  • 可以通過“關(guān)聯(lián)對(duì)象”機(jī)制來把兩個(gè)對(duì)象連起來。
  • 定義關(guān)聯(lián)對(duì)象時(shí)可指定內(nèi)存管理語義,用以模仿定義屬性時(shí)所采用的“擁有關(guān)系”與“非用有關(guān)系”。
  • 只有在其他方法不可行時(shí)才應(yīng)選用關(guān)聯(lián)對(duì)象,因?yàn)檫@種做法通常會(huì)引入難于查找的bug。

11、理解objc_msgSend的作用

id returmValue = [someObject messageName:parametter];
someObject叫做“接收者”,messageName叫做“選擇子(selector)”。選擇子和參數(shù)合起來稱為“消息(message)”。編譯器看到后將其轉(zhuǎn)換成標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用,所調(diào)的函數(shù)乃是消息傳遞機(jī)制中的核心函數(shù),叫做objc_msgSend,其“原型”如下:

void objc_msgSend(id self,SEL cmd,……)
第一個(gè)參數(shù)是接收者,第二個(gè)參數(shù)是選擇子,后續(xù)參數(shù)就是消息中那些參數(shù),其順序不變。

在實(shí)際開發(fā)中,大家無須擔(dān)心這一問題,不過應(yīng)該了解其底層工作原理。

12、理解消息轉(zhuǎn)發(fā)機(jī)制

  • 若對(duì)象無法響應(yīng)某個(gè)選擇子,則進(jìn)入消息轉(zhuǎn)發(fā)流程
  • 通過運(yùn)行期的動(dòng)態(tài)方法解析功能,我們可以在需要用到某個(gè)方法時(shí)再將其加入類中。
  • 對(duì)象可以把其無法解讀的某些選擇子轉(zhuǎn)交給其他對(duì)象來處理
  • 經(jīng)過上述兩步之后,如果還是沒辦法處理選擇子,那就啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容