亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Objective-C 2.0 with Cocoa Foundation--- 8,

系統(tǒng) 2108 0

8,類方法以及私有方法

本系列講座有著很強(qiáng)的前后相關(guān)性,如果你是第一次閱讀本篇文章,為了更好的理解本章內(nèi)容,筆者建議你最好從本系列講座的第1章開始閱讀,請(qǐng)點(diǎn)擊 這里

?

Objective-C里面區(qū)別于實(shí)例方法,和Java或者C++一樣,也支持類方法。類方法(Class Method) 有時(shí)被稱為工廠方法(Factory Method)或者方便方法(Convenience method)。工廠方法的稱謂明顯和一般意義上的工廠方法不同,從本質(zhì)上來(lái)說(shuō),類方法可以獨(dú)立于對(duì)象而執(zhí)行,所以在其他的語(yǔ)言里面類方法有的時(shí)候被稱為 靜態(tài)方法。就像@interface曾經(jīng)給我們帶來(lái)的混亂一樣,現(xiàn)在我們就不去追究和爭(zhēng)論工廠方法的問(wèn)題了,我們看到Objective-C的文章說(shuō)工廠 方法,就把它當(dāng)作類方法好了。

?

在Objective-C里面,最受大家歡迎的類方法應(yīng)該是alloc,我們需要使用alloc來(lái)為我們的對(duì)象分配內(nèi)存。可以想象,如果沒有alloc,我們將要如何來(lái)為我們的類分配內(nèi)存!

?

和其他的語(yǔ)言類似,下面是類方法的一些規(guī)則,請(qǐng)大家務(wù)必記住。

?

1,類方法可以調(diào)用類方法。

2,類方法不可以調(diào)用實(shí)例方法,但是類方法可以通過(guò)創(chuàng)建對(duì)象來(lái)訪問(wèn)實(shí)例方法。

3,類方法不可以使用實(shí)例變量。類方法可以使用self,因?yàn)閟elf不是實(shí)例變量。

4,類方法作為消息,可以被發(fā)送到類或者對(duì)象里面去(實(shí)際上,就是可以通過(guò)類或者對(duì)象調(diào)用類方法的意思)。

?

如果大家觀察一下Cocoa的類庫(kù),會(huì)發(fā)現(xiàn)類方法被大量的應(yīng)用于方便的對(duì)象創(chuàng)建和操作對(duì)象的,考慮到類方法的上述的特性,同學(xué)們?cè)谠O(shè)計(jì)自己的類的時(shí)候,為 了謀求這種方便,可以考慮使用類方法來(lái)創(chuàng)建或者操作對(duì)象。筆者認(rèn)為,這個(gè)就是類方法的潛規(guī)則,在本章的范例程序里面,筆者將要遵守這個(gè)潛規(guī)則。

?

在上一章我們講了一下實(shí)例變量的作用域,實(shí)例變量的作用域的方式和其他面向?qū)ο蟮恼Z(yǔ)言沒有什么不同。對(duì)于方法,非常遺憾的是,Objective- C并沒有為我們提供諸如public,private和protected這樣的限定,這就意味著在Objective-C里面,從理論上來(lái)說(shuō)所有的方法 都是公有的。但是,我們可以利用Objective-C的語(yǔ)言的特性,我們自己來(lái)實(shí)現(xiàn)方法的私有化。當(dāng)然我們自己的私有化手段沒有得到任何的編譯器的支 持,只是告訴使用者:“這是一個(gè)私有的方法,請(qǐng)不要使用這個(gè)方法”。所以,無(wú)論作為類的設(shè)計(jì)者和使用者都應(yīng)該清楚在Objective-C里面的方法私有 化的所有手段,這樣就在類的設(shè)計(jì)者和使用者之間達(dá)成了一種默契,這種方式明顯不是Objective-C語(yǔ)法所硬性規(guī)定的,所以也可以把這種手法成為一種 潛規(guī)則。

?

關(guān)于潛規(guī)則 經(jīng)常看英文文檔的同學(xué),應(yīng)該可以遇到這樣一個(gè)詞,de facto standard,也就是筆者所說(shuō)的潛規(guī)則。

?

本章所述的方法的私有化是一種有缺陷的手段,有一定的風(fēng)險(xiǎn)而且也沒有完全實(shí)現(xiàn)私有化,在后面的章節(jié)里面筆者會(huì)陸續(xù)的給出其他的實(shí)現(xiàn)方法私有化的方法。

?

另外,Objective-C里面有一個(gè)其他不支持指針的語(yǔ)言沒有的一個(gè)動(dòng)態(tài)特性,那就是程序在執(zhí)行的時(shí)候,可以動(dòng)態(tài)的替換類的手段。動(dòng)態(tài)的方法替 換有很多種應(yīng)用,本章實(shí)現(xiàn)了一個(gè)類似java里面的final函數(shù)。和final函數(shù)不同的是,如果子類重寫了這個(gè)方法,編譯器不會(huì)報(bào)錯(cuò),但是執(zhí)行的時(shí)候 總是執(zhí)行的你的超類的方法。

?

類方法,方法私有化和動(dòng)態(tài)方法替換將是本章的主題。

8.1,本章程序的執(zhí)行結(jié)果

在本章里面,我們將要繼續(xù)使用我們?cè)诘?章已經(jīng)構(gòu)筑好的類Cattle和Bull。

?

筆者在這里暫時(shí)違反一下不修改已經(jīng)生效的代碼規(guī)則改寫了一下Cattle和Bull類,在里面追加了一些類方法,用于創(chuàng)建Cattle系列的對(duì)象。

?

筆者也改寫了Cattle的頭文件用來(lái)實(shí)現(xiàn)方法的私有化。

?

面向?qū)ο蟮某绦蛴幸粋€(gè)很大的特色就是動(dòng)態(tài)性,但是由于某種原因我們?cè)谠O(shè)計(jì)超類的時(shí)候,也許會(huì)考慮把某個(gè)方法設(shè)定成為靜態(tài)的,這樣就有了諸如 final的概念。在本章我們將要使用動(dòng)態(tài)的方法替換來(lái)實(shí)現(xiàn)這個(gè)功能。我們將要構(gòu)筑一個(gè)新類,名字叫做UnknownBull,我們使用動(dòng)態(tài)方法替換導(dǎo)致 即使UnknownBull重載了Cattle類的saySomething,但是向UnknownBull發(fā)送saySomething的時(shí)候,仍然執(zhí) 行的是Cattle的saySomething。本章程序的執(zhí)行結(jié)果請(qǐng)參照下圖:

?

Objective-C 2.0 with Cocoa Foundation--- 8,類方法以及私有方法 ?

圖8-1,本章程序的執(zhí)行結(jié)果。

?

本章程序可以點(diǎn)擊 這里 下載。

8.2,實(shí)現(xiàn)步驟

第一步,按照我們?cè)诘?章所述的方法,新建一個(gè)項(xiàng)目,項(xiàng)目的名字叫做07-InitWithAndIvarScope。如果你是第一次看本篇文章,請(qǐng)到 這里 參看第二章的內(nèi)容。

?

第二步,按照我們?cè)诘?章的4.2節(jié)的第二,三,四步所述的方法,把在第4章已經(jīng)使用過(guò)的“Cattle.h”,“Cattle.m”,“Bull.h”還有“Bull.m”, 導(dǎo)入本章的項(xiàng)目里面。

?

第三步,打開“Cattle.h”和“Cattle.m”,分別修改成為下面的代碼并且保存:

    #import <Foundation/Foundation.h>

@interface Cattle : NSObject {
    int legsCount;
}
- (void)saySomething;
+ (id) cattleWithLegsCountVersionA:(int) count;
+ (id) cattleWithLegsCountVersionB:(int) count;
+ (id) cattleWithLegsCountVersionC:(int) count;
+ (id) cattleWithLegsCountVersionD:(int) count;
@end

#import "Cattle.h"
#import <objc/objc-class.h>

@implementation Cattle
-(void) saySomething
{
    NSLog(@"Hello, I am a cattle, I have %d legs.", legsCount);
}
-(void) setLegsCount:(int) count
{
    legsCount = count;
}
+ (id) cattleWithLegsCountVersionA:(int) count
{
    id ret = [[Cattle alloc] init];
    //NEVER DO LIKE BELOW
    //legsCount = count;
    [ret setLegsCount:count];
    return [ret autorelease];
}
+ (id) cattleWithLegsCountVersionB:(int) count
{
    id ret = [[[Cattle alloc] init] autorelease];
    [ret setLegsCount:count];
    return ret;    
}
+ (id) cattleWithLegsCountVersionC:(int) count
{
    id ret = [[self alloc] init];
    [ret setLegsCount:count];
    return [ret autorelease];
}
+ (id) cattleWithLegsCountVersionD:(int) count 
{
    id ret = [[self alloc] init];
 [ret setLegsCount:count];
    
    if([self class] == [Cattle class])
       return [ret autorelease];

    SEL sayName = @selector(saySomething);
    Method unknownSubClassSaySomething = class_getInstanceMethod([self class], sayName);
    //Change the subclass method is RUDE!
    Method cattleSaySomething = class_getInstanceMethod([Cattle class], sayName);
    //method_imp is deprecated since 10.5
    unknownSubClassSaySomething->method_imp = cattleSaySomething->method_imp;

    return [ret autorelease];
}
@end
  
??

第四步,打開“Bull.h”和“Bull.m”,分別修改成為下面的代碼并且保存:

    #import <Foundation/Foundation.h>
#import "Cattle.h"

@interface Bull : Cattle {
    NSString *skinColor;
}
- (void)saySomething;
- (NSString*) getSkinColor;
- (void) setSkinColor:(NSString *) color;
+ (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor;
@end

 
#import "Bull.h"

@implementation Bull
-(void) saySomething
{
    NSLog(@"Hello, I am a %@ bull, I have %d legs.", [self getSkinColor],legsCount);
}
-(NSString*) getSkinColor
{
    return skinColor;
}
- (void) setSkinColor:(NSString *) color
{
    skinColor = color;
}
+ (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor
{
    id ret = [self cattleWithLegsCountVersionC:count];
    [ret setSkinColor:theColor];
    //DO NOT USE autorelease here!
    return ret;
}
@end
  
??

第五步,創(chuàng)建一個(gè)新類,名字叫做“UnknownBull”,然后分別打開“UnknownBull.h”和“UnknownBull.m”,分別修改成為下面的代碼并且保存:

    #import <Foundation/Foundation.h>
#import "Bull.h"

@interface UnknownBull : Bull {

}
-(void)saySomething;
@end

 
#import "UnknownBull.h"

@implementation UnknownBull
-(void)saySomething
{
    NSLog(@"Hello, I am an unknown bull.");
}
@end
  

?

第六步,打開“08-Class_Method_And_Private_Method.m” ,修改成為下面的樣子并且保存

      #import <Foundation/Foundation.h>
#import "Cattle.h"
#import "Bull.h"
#import "UnknownBull.h"
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    id cattle[5];
    cattle[0] = [Cattle cattleWithLegsCountVersionA:4];
    cattle[1] = [Bull cattleWithLegsCountVersionB:4];
    cattle[2] = [Bull cattleWithLegsCountVersionC:4];
    cattle[3] = [Bull bullWithLegsCount:4 bullSkinColor:@"red"];
    cattle[4] = [UnknownBull cattleWithLegsCountVersionD:4];
    
    for(int i = 0 ; i < 5 ; i++)
    {
        [cattle[i] saySomething];
    }
    [pool drain];
    return 0;
}
    
??

第七步,選擇屏幕上方菜單里面的“Run”,然后選擇“Console”,打開了Console對(duì)話框之后,選擇對(duì)話框上部中央的“Build and Go”,如果不出什么意外的話,那么應(yīng)該出現(xiàn)入圖8-1所示的結(jié)果。如果出現(xiàn)了什么意外導(dǎo)致錯(cuò)誤的話,那么請(qǐng)仔細(xì)檢查一下你的代碼。如果經(jīng)過(guò)仔細(xì)檢查發(fā)現(xiàn) 還是不能執(zhí)行的話,可以到 這里 下載筆者為同學(xué)們準(zhǔn)備的代碼。 如果筆者的代碼還是不能執(zhí)行的話,請(qǐng)告知筆者。

8.2,方法的私有化

在講述方法私有化之前,我們首先要提到一個(gè)Objective-C里面的一個(gè)概念,動(dòng)態(tài)類型和靜態(tài)類型。

所謂的動(dòng)態(tài)類型,就是使用id來(lái)定義一個(gè)對(duì)象,比如說(shuō)

      id cattle = [[Cattle alloc] init];
    
?

所謂的靜態(tài)類型,就是使用已知變量的的類型來(lái)定義對(duì)象,比如說(shuō)

      Cattle cattle = [[Cattle alloc] init];
    
?

動(dòng)態(tài)類型和靜態(tài)類型各有好處,動(dòng)態(tài)類型實(shí)現(xiàn)了多態(tài)性,使用靜態(tài)類型的時(shí)候編譯器會(huì)為你檢查一下也許會(huì)出現(xiàn)危險(xiǎn)的地方,比如說(shuō)向一個(gè)靜態(tài)類型的對(duì)象發(fā)送一個(gè)它沒有定義的消息等等。

?

好的,我們現(xiàn)在打開“cattle.h”,大家可以發(fā)現(xiàn),和以前的版本相比,我們的“cattle.h”少了一個(gè)方法的定義,那就是-(void) setLegsCount:(int) count;。筆者在本章的范例程序里面實(shí)現(xiàn)私有方法的手段比較簡(jiǎn)單,直接把-(void) setLegsCount:(int) count從“cattle.h”給刪除掉了。

?

大家打開““cattle.m”,可以看到里面-(void) setLegsCount:(int) count是有實(shí)現(xiàn)部分的。實(shí)現(xiàn)部分和過(guò)去的版本沒有任何區(qū)別的。

?

我們本章里面講述的實(shí)現(xiàn)方法私有化的手段,就是從頭文件當(dāng)中不寫方法的聲明。這樣做會(huì)導(dǎo)致如下幾個(gè)現(xiàn)象

?

1,在類的實(shí)現(xiàn)文件.m里面,你可以向平常一樣使用[self setLegsCount:4] 來(lái)發(fā)送消息,但是確省設(shè)定的編譯器會(huì)很不禮貌的給你一個(gè)警告。

?

2,你可以向Cattle以及從Cattle繼承的類的靜態(tài)對(duì)象發(fā)送setLegsCount:4的消息,但是同樣,確省設(shè)定的編譯器會(huì)很不禮貌的給你一個(gè)警告。

?

3,你可以向Cattle以及從Cattle繼承的類的動(dòng)態(tài)對(duì)象發(fā)送setLegsCount:4的消息,編譯器不會(huì)向你發(fā)送任何警告的。

說(shuō)到這里,同學(xué)們也許會(huì)覺得這一節(jié)的方法私有化有一點(diǎn)奇怪,因?yàn)樵谏厦娴牡诙l里面,不能阻止對(duì)對(duì)象的私有方法進(jìn)行調(diào)用。令我們更為惱火的是,居然在我們自己的類的實(shí)現(xiàn)文件里面需要調(diào)用的時(shí)候產(chǎn)生諸如第一條的警告!

?

讓我們冷靜一下。

?

我們說(shuō),在面向?qū)ο蟮某绦蚶锩妫话愣灶惖氖褂谜咧魂P(guān)心接口,不關(guān)心實(shí)現(xiàn)的。當(dāng)我們類的實(shí)現(xiàn)部分的某個(gè)方法,在頭文件里面沒有定義的話,那么由于 我們的類的使用者只是看頭文件,所以他不應(yīng)該是用我們定義的所謂的私有方法的。這一點(diǎn),對(duì)于其他的語(yǔ)言來(lái)說(shuō)也是一樣的,其他的語(yǔ)言的私有方法和變量,如果 我們把它們改為public,或者我們不修改頭文件,使用指針也可以強(qiáng)行的訪問(wèn)到私有的變量和方法的,從這個(gè)角度上來(lái)說(shuō),私有化的方法和變量也只不過(guò)是一 個(gè)擺設(shè)而已,沒有人可以阻止我們?nèi)ピL問(wèn)他們,探求埋藏在里面的奧秘。所謂的私有化只不過(guò)是一個(gè)潛規(guī)則而已,在正常的時(shí)候,我們大家都會(huì)遵守這個(gè)潛規(guī)則的。 但是被逼無(wú)奈走投無(wú)路的時(shí)候我們也許會(huì)除了訪問(wèn)私有的東西無(wú)可選擇。但是也不能過(guò)分,我們顯然不可以把訪問(wèn)私有變量和函數(shù)當(dāng)作一種樂趣。

?

說(shuō)到這里,我想大家應(yīng)該可以理解這種私有化方法的定義了。它只不過(guò)是一種信號(hào),告訴類的使用者,“這是一個(gè)私有的函數(shù),請(qǐng)不要使用它,否則后果自 負(fù)” 。我們?cè)诳吹絼e人的代碼的時(shí)候看到了這種寫法的時(shí)候,或者別人看到我們的代碼的時(shí)候,大家都需要做到相互理解對(duì)方的隱藏私有部分的意圖。還是還是這句話, 在大多數(shù)時(shí)候,請(qǐng)不要破壞潛規(guī)則。

8.3, 類方法

我們現(xiàn)在轉(zhuǎn)到本章最重要的主題,類方法。我們將要首先關(guān)注一下類方法的聲明,現(xiàn)在請(qǐng)同學(xué)們打開"Cattle.h"文件,可以發(fā)現(xiàn)下面的代碼:

      + (id) cattleWithLegsCountVersionA:(int) count;
+ (id) cattleWithLegsCountVersionB:(int) count;
+ (id) cattleWithLegsCountVersionC:(int) count;
+ (id) cattleWithLegsCountVersionD:(int) count;

    
?

類方法和實(shí)例方法在聲明上的唯一的區(qū)別就是,以加號(hào)+為開始,其余的部分是完全一致的。 筆者在這里定義了4個(gè)不同版本的類方法,從功能上來(lái)說(shuō)都是用來(lái)返回Cattle類或者其子類的對(duì)象的,其中 cattleWithLegsCountVersionA到C是我們這一節(jié)講解的重點(diǎn)。

?

讓我們首先打開“Cattle.m” ,關(guān)注一下下面的代碼:

    + (id) cattleWithLegsCountVersionA:(int) count
{
    id ret = [[Cattle alloc] init];
    //NEVER DO LIKE BELOW
    //legsCount = count;
    [ret setLegsCount:count];
    return [ret autorelease];
}
+ (id) cattleWithLegsCountVersionB:(int) count
{
    id ret = [[[Cattle alloc] init] autorelease];
    [ret setLegsCount:count];
    return ret;    
}

  
?

我們需要使用類方法創(chuàng)建對(duì)象,所以在第3行,我們使用了我們比較熟悉的對(duì)象的創(chuàng)建的方法創(chuàng)建了一個(gè)對(duì)象。大家注意一下第5行,由于類方法是和對(duì)象是 脫離的所以我們是無(wú)法在類方法里面使用實(shí)例變量的。第6行,由于我們創(chuàng)建了對(duì)象ret,所以我們可以向ret發(fā)送setLegsCount:這個(gè)消息,我 們通過(guò)這個(gè)消息,設(shè)定了Cattle的legsCount實(shí)例變量。在第7行,我們遇到了一個(gè)新的朋友,autorelease。我們?cè)陬惙椒ɡ锩鎰?chuàng)建了 一個(gè)對(duì)象,當(dāng)我們返回了這個(gè)對(duì)象之后,類方法也隨之結(jié)束,類方法結(jié)束就意味著在我們寫的類方法里面,我們失去了對(duì)這個(gè)對(duì)象的參照,也就永遠(yuǎn)無(wú)法在類方法里 面控制這個(gè)對(duì)象了。在Objective-C里面有一個(gè)規(guī)則,就是誰(shuí)創(chuàng)建的對(duì)象,那么誰(shuí)就有負(fù)責(zé)管理這個(gè)對(duì)象的責(zé)任,類方法結(jié)束之后,除非和類的使用者商 量好了讓類的使用者釋放內(nèi)存,否則我們無(wú)法直接的控制這個(gè)過(guò)程。

?

記憶力好的同學(xué)應(yīng)該可以回憶起來(lái),筆者曾經(jīng)在第二章提到過(guò)一種延遲釋放內(nèi)存的技術(shù),這個(gè)就是autorelease。關(guān)于autorelease以 及其他的內(nèi)存管理方法,我們將在下一章放到一起講解。到這里大家記住,使用類方法的潛規(guī)則是你要使用類方法操作對(duì)象,當(dāng)你需要使用類方法創(chuàng)建一個(gè)對(duì)象的時(shí) 候,那么請(qǐng)?jiān)陬惙椒ɡ锩婕由蟖utorelease。

?

我們來(lái)看看cattleWithLegsCountVersionB的實(shí)現(xiàn)部分的代碼,和cattleWithLegsCountVersionA 唯一區(qū)別就是我們?cè)趧?chuàng)建的時(shí)候就直接的加上了autorelease。這樣符合創(chuàng)建對(duì)象的時(shí)候“一口氣”的把所有需要的方法都寫到一起的習(xí)慣,采取什么方 式取決于個(gè)人喜好。

?

我們?cè)俅蜷_“08-Class_Method_And_Private_Method.m”,參看下面的代碼

    cattle[0] = [Cattle cattleWithLegsCountVersionA:4];
cattle[1] = [Bull cattleWithLegsCountVersionB:4];

  
??

我們?cè)诨仡^看看本章程序的執(zhí)行結(jié)果,心細(xì)的同學(xué)也許發(fā)現(xiàn)了一個(gè)很嚴(yán)重的問(wèn)題,我們?cè)诘?行代碼里面想要返回一個(gè)Bull的對(duì)象,但是輸出的時(shí)候卻變 成了Cattle,原因就是我們?cè)赾attleWithLegsCountVersionB里面創(chuàng)建對(duì)象的時(shí)候,使用了id ret = [[[Cattle alloc] init] autorelease]。由于Bull里面沒有重寫cattleWithLegsCountVersionB,所以除非我們重寫 cattleWithLegsCountVersionB否則我們向Bull發(fā)送cattleWithLegsCountVersionB這個(gè)類方法的時(shí) 候,只能得到一個(gè)Cattle的對(duì)象。我們可以要求我們的子類的設(shè)計(jì)者在他們的子類當(dāng)中重寫cattleWithLegsCountVersionB,但 是這樣明顯非常笨拙,失去了動(dòng)態(tài)的特性。我們當(dāng)然有辦法解決這個(gè)問(wèn)題,現(xiàn)在請(qǐng)大家回到“Cattle.m”,參照下列代碼:

    + (id) cattleWithLegsCountVersionC:(int) count
{
     id ret = [[self alloc] init];
     [ret setLegsCount:count];
     return [ret autorelease];
}

  
??

我們的解決方案就在第3行,我們不是用靜態(tài)的Cattle,而是使用self。說(shuō)到這里也許大家有些糊涂了,在其他的語(yǔ)言當(dāng)中和self比較類似的 是this指針,但是在Objective-C里面self和this有些不大一樣,在類函數(shù)里面的self實(shí)際上就是這個(gè)類本身。大家可以打開 debugger觀察一下,self的地址就是Bull的Class的地址。所以程序執(zhí)行到上面的代碼的第3行的時(shí)候,實(shí)際上就等同于id ret = [[[Bull class] alloc] init];

?

我們可以在類方法里面使用self,我們可否通過(guò)使用self->legsCount來(lái)訪問(wèn)實(shí)例變量呢?答案是不可以,因?yàn)樵谶@個(gè)時(shí)候?qū)ο鬀]有被創(chuàng)建也就是說(shuō),沒有為legsCount分配內(nèi)存,所以無(wú)法訪問(wèn)legsCount。

?

由于Bull類在程序被調(diào)入內(nèi)存的時(shí)候就已經(jīng)初始化好了,Bull類里面的實(shí)例函數(shù)應(yīng)該被放到了代碼段,所以從理論上來(lái)說(shuō),我們可以通過(guò)使用 [self setLegsCount:count]來(lái)調(diào)用實(shí)例方法的,但是不幸的是Objective-C沒有允許我們這樣做,我們?cè)陬惙椒ㄖ惺褂胹elf來(lái)作為消 息的接收者的時(shí)候,消息總是被翻譯成為類方法,如果發(fā)送實(shí)例方法的消息的話,會(huì)在執(zhí)行的時(shí)候找不到從而產(chǎn)生異常。這樣做是有一定的道理的,因?yàn)橐话愣裕?實(shí)例方法里面難免要使用實(shí)例變量,在類方法當(dāng)中允許使用實(shí)例方法,實(shí)際上也就允許使用實(shí)例變量。

?

關(guān)于self 大家需要記住下面的規(guī)則:

1,實(shí)例方法里面的self,是對(duì)象的首地址。

2,類方法里面的self,是Class.

盡管在同一個(gè)類里面的使用self,但是self卻有著不同的解讀。在類方法里面的self,可以翻譯成class self;在實(shí)例方法里面的self,應(yīng)該被翻譯成為object self。在類方法里面的self和實(shí)例方法里面的self有著本質(zhì)上的不同,盡管他們的名字都叫self。

?

請(qǐng)同學(xué)們?cè)俅位氐綀D8-1,可以發(fā)現(xiàn)通過(guò)使用神奇的self,我們動(dòng)態(tài)的創(chuàng)建了Bull類的對(duì)象。但是等一下,我們的程序并不完美,因?yàn)锽ull類 的skinColor并沒有得到初始化,所以導(dǎo)致了null的出現(xiàn)。我們?cè)谠O(shè)計(jì)Cattle類也就是Bull的超類的時(shí)候,明顯我們無(wú)法預(yù)測(cè)到Bull類 的特征。消除這種問(wèn)題,我們可以在得到了Bull對(duì)象之后使用setSkinColor:來(lái)設(shè)定顏色,當(dāng)然我們也可以直接寫一個(gè)Bull類的方法,來(lái)封裝 這個(gè)操作,請(qǐng)同學(xué)們打開“Bull.h”:

?

+ ?(id)?bullWithLegsCount:( int )?count?bullSkinColor:(NSString * )?theColor;

我們追加了一個(gè)類方法, bullWithLegsCount:bullSkinColor:用于創(chuàng)建Bull對(duì)象,請(qǐng)同學(xué)們打開“Bull.m”:

?

    + (id) bullWithLegsCount:(int) count bullSkinColor:(NSString*) theColor
{
    id ret = [self cattleWithLegsCountVersionC:count];
   [ret setSkinColor:theColor];
    //DO NOT USE autorelease here!
    return ret;
}

  
??

上面這一段代碼相信大家都可以看明白,筆者就不在這里贅述了。但是筆者需要強(qiáng)調(diào)一點(diǎn),在這里我們不需要調(diào)用autorelease的,因?yàn)槲覀儧]有在這里創(chuàng)建任何對(duì)象。

?

經(jīng)過(guò)了這個(gè)改造,通過(guò)在“08-Class_Method_And_Private_Method.m”里面我們使用

?

cattle[ 3 ]? = ?[Bull?bullWithLegsCount: 4 ?bullSkinColor: @" red " ];

?

使得我們的代碼終于正常了,請(qǐng)參照?qǐng)D8-1的第4行輸出。

8.4,使用動(dòng)態(tài)方法替換實(shí)現(xiàn)final功能

首先請(qǐng)同學(xué)們打開“Cattle.m”,參照下面的代碼片斷:

    + (id) cattleWithLegsCountVersionD:(int) count 
{
    id ret = [[self alloc] init];
    [ret setLegsCount:count];
    
    if([self class] == [Cattle class])
       return [ret autorelease];

    SEL sayName = @selector(saySomething);
    Method unknownSubClassSaySomething = class_getInstanceMethod([self class], sayName);
    //Change the subclass method is RUDE!
    Method cattleSaySomething = class_getInstanceMethod([Cattle class], sayName);
    //method_imp is deprecated since 10.5
    unknownSubClassSaySomething->method_imp = cattleSaySomething->method_imp;

    return [ret autorelease];
}
@end
  
?

在cattleWithLegsCountVersionD里面,我們將要通過(guò)使用動(dòng)態(tài)的方法替換技術(shù)來(lái)實(shí)現(xiàn)final方法。


第3,4行代碼,是用于創(chuàng)建Cattle或者從Cattle類繼承的對(duì)象,并且設(shè)定實(shí)例變量legsCount。


第6,7行代碼,是用來(lái)判斷調(diào)用這個(gè)類方法的self是不是cattle,如果是cattle的話,那么就直接返回,因?yàn)槲覀円谶@個(gè)方法里面把子 類的saySomething替換成為Cattle的saySomething,如果類是Cattle的話,那么很明顯,我們不需要做什么事情的。


第9行代碼是老朋友了,我們需要得到方法的SEL。

?

第10行和第12行,我們需要通過(guò)Objective-C的一個(gè)底層函數(shù),class_getInstanceMethod來(lái)取得方法的數(shù)據(jù)結(jié)構(gòu) Method。讓我們把鼠標(biāo)移動(dòng)到Method關(guān)鍵字上面,點(diǎn)擊鼠標(biāo)右鍵盤,選擇“Jump to definition”,我們可以看到在文件“objc-class.h”里面的Method的定義。Method實(shí)際上是類方法在Class里面的數(shù)據(jù) 結(jié)構(gòu),系統(tǒng)會(huì)使用Method的信息來(lái)構(gòu)筑Class的信息。在Method類型的聲明里面,我們看到了下面的代碼

      typedef struct objc_method *Method;

struct objc_method {
  SEL method_name;
  char *method_types;
  IMP method_imp;
};
    
??

其中SEL和IMP我們已經(jīng)很熟悉了, method_types是方法的類型信息,Objective-C使用一些預(yù)定義的宏來(lái)表示方法的類型,然后把這些信息放到 method_types里面。

?

需要強(qiáng)調(diào)的是,蘋果在10.5之后就降級(jí)了很多Objective-C 底層的函數(shù),并且在64位的應(yīng)用當(dāng)中使得這些函數(shù)失效,筆者對(duì)剝奪了眾多程序員的自由而感到遺憾。

?

第14行的代碼,我們把子類的函數(shù)指針的地址替換成為Cattle類的saySomething,這樣無(wú)論子類是否重寫saySomething, 執(zhí)行的時(shí)候由于runtime需要找到方法的入口地址,但是這個(gè)地址總是被我們替換為Cattle的saySomething,所以子類通過(guò) cattleWithLegsCountVersionD取得對(duì)象之后,總是調(diào)用的Cattle的saySomething,也就實(shí)現(xiàn)了final。當(dāng) 然,這種方法有些粗魯,我們強(qiáng)行的不顧后果的替換了子類的重寫。

?

重要 本節(jié)提到的final的實(shí)現(xiàn)方法,沒有任何蘋果官方的文檔建議這樣做,純屬筆者自創(chuàng)僅供大家參考,如果使用風(fēng)險(xiǎn)自擔(dān)。

?

替換的結(jié)果,就是雖然我們?cè)凇?8-Class_Method_And_Private_Method.m”里面的cattle[4]l里面使用 UnknownBull是圖返回UnknownBull對(duì)象,我們也確實(shí)得到了UnknownBull對(duì)象,但是不同的是,我們?cè)?cattleWithLegsCountVersionD里面貍貓換太子,把UnknownBull的saySomething變成了Cattle的 saySomething。

?

讓我們回到圖8-1,我們發(fā)現(xiàn)最后一行的輸出為Cattle的saySomething。

?

關(guān)于final的實(shí)現(xiàn)方式,我們當(dāng)然可以使用一個(gè)文明的方法來(lái)告知子類的使用者,我們不想讓某個(gè)方法被重寫。我們只需要定義一個(gè)宏

?

#define ?FINAL

類的使用者看到這個(gè)FINAL之后,筆者相信在絕大多數(shù)時(shí)候,他會(huì)很配合你不會(huì)重寫帶FINAL定義的方法的。

8.5,本章總結(jié)

我們?cè)诒菊吕锩嬷v述了方法私有化,類方法的定義和使用,動(dòng)態(tài)方法替換等技術(shù)手段,也給大家強(qiáng)調(diào)和澄清了self的概念。
更重要的是,筆者向大家介紹了一些潛規(guī)則,希望大家可以遵守。
非常感謝大家這些天對(duì)我的鼓勵(lì)以及支持!

?

來(lái)源: http://www.cnblogs.com/yaski/archive/2009/04/29/1444035.html

?

?

?

    
      前置加號(hào)(+)的方法為類方法,這類方法是可以直接用類名來(lái)調(diào)用的,它的作用主要是創(chuàng)建一個(gè)實(shí)例。
前置減號(hào)(-)的方法為實(shí)例方法,必須使用類的實(shí)例才可以調(diào)用的。
    
  

?

?

?

?

Objective-C 2.0 with Cocoa Foundation--- 8,類方法以及私有方法


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 日韩欧美毛片 | 欧美精品99久久久久久人 | 免费视频一区二区三区四区 | 午夜国产精品影院在线观看 | 九九爱www高清免费人成 | 欧美一区二区三区在线视频 | 日韩中文字幕在线免费观看 | 免费国产a| 正在播放国产乱子伦视频 | 青青热久久久久综合精品 | 日本特黄一级午夜剧场毛片 | 欧美综合激情 | 乱人伦精品一区二区 | 国产福利99 | 黄色在线视频网 | 久久精品是免费100 久久精品首页 | 久久精品女人天堂 | 久久4| 香蕉黄色网 | 成人精品一级毛片 | 四虎最新永久免费视频 | 精品视频香蕉尹人在线 | 日本一级特黄毛片免费视频9 | 一级毛片在线 | 久久99视频| 亚洲第一页在线播放 | 色综合夜夜嗨亚洲一二区 | 中国免费一级毛片 | 91精品国产91久久久久 | 久久国产欧美日韩高清专区 | 在线视频 国产交换 | 西西人体大胆77777视频 | 综合久久久久综合体桃花网 | 国产精品亚洲片在线观看麻豆 | 国产区一区二 | 69成人做爰视频在线观看 | 亚洲 欧洲 自拍 另类 校园 | 国产日韩精品一区二区 | 四虎国产永久免费久久 | 一区二区日韩欧美 | 92国产福利久久青青草原 |