Methods - 方法

來源于 Ry’s Objective-C Tutorial - RyPress

一個學習Objective-C基礎知識的網站.

個人覺得很棒,所以決定抽時間把章節翻譯一下.

本人的英語水平有限,有讓大家誤解或者迷惑的地方還請指正.

原文地址:http://rypress.com/tutorials/objective-c/methods.html

僅供學習,如有轉摘,請注明出處.


方法代表著一個對象如何執行操作.它們與代表著對象數據的屬性在邏輯上等同.你可以把方法當做附加給對象的函數,但他們有著不同的語法.

在這個模塊,我們將探討OC方法的命名規約,(這些規約)對有著C++,Java,Python以及其他類似語言的開發經驗人員來說,會比較frustrating(難受).我們也會簡要的討論OC的訪問修飾符(用來設置訪問權限),并學習怎樣使用selectors(選擇器)關聯方法.

命名規約

OC的方法旨在(被設計成)移除API中任何含糊之處,所以導致了方法名很冗長(啰嗦的嚇人),但是不可否認的是,描述性很強.遵守以下三條規則以實現OC方法的命名.
1.不要使用縮寫
2.明確規定方法自身的參數名
3.明確描述方法的返回值
在你閱讀下面的Car類接口時把這些規則記在心里.

// Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

// Accessors
- (BOOL)isRunning;
- (void)setRunning:(BOOL)running;
- (NSString *)model;
- (void)setModel:(NSString *)model;

// Calculated values
- (double)maximumSpeed;
- (double)maximumSpeedUsingLocale:(NSLocale *)locale;

// Action methods
- (void)startEngine;
- (void)driveForDistance:(double)theDistance;
- (void)driveFromOrigin:(id)theOrigin toDestination:(id)theDestination;
- (void)turnByAngle:(double)theAngle;
- (void)turnToAngle:(double)theAngle;

// Error handling methods
- (BOOL)loadPassenger:(id)aPassenger error:(NSError **)error;

// Constructor methods
- (id)initWithModel:(NSString *)aModel;
- (id)initWithModel:(NSString *)aModel mileage:(double)theMileage;

// Comparison methods
- (BOOL)isEqualToCar:(Car *)anotherCar;
- (Car *)fasterCar:(Car *)anotherCar;
- (Car *)slowerCar:(Car *)anotherCar;

// Factory methods
+ (Car *)car;
+ (Car *)carWithModel:(NSString *)aModel;
+ (Car *)carWithModel:(NSString *)aModel mileage:(double)theMileage;

// Singleton methods
+ (Car *)sharedCar;

@end
縮寫

讓方法能夠被理解,被預料(可以被猜測)的最簡單方式就是避免縮寫.大多數OC編程人員都希望方法能被完全寫出,這是所有標準框架里的規約,從[Foundation](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/ _classic/_index.html)到UIKit都是這樣.這就是上述接口中選擇使用maximumSpeed取代更簡寫的maxSpeed的原因.

參數

另一個清楚(反映)OC冗長設計哲學的例子就是方法參數的命名規約.C++,Java以及其他類似風格的語言中,將參數與方法分離成單獨的實體,而OC的方法名實則包括它所有的參數名.

舉個例子,在C++中為了讓Car(方向)轉90°,你也許會使用這樣turn(90)的方法.但是,OC覺著這太含糊了.搞不清楚turn()使用什么樣的參數-可能是一個新的方向角,也可能是在調整(增加)你當前的方向角.OC方法通過一個介詞來描述讓它明確.這種方式實現的API保證了方法永遠不會被誤用:它(方法)要么是turnByAngle:90.要么是turnToAngle:90.

當一個方法可接受多個參數時,那么每個參數名都被包含在方法名中.上面的例子中,initWithModel:mileage:方法把Model和mileage都帶上了.正如等會我們所見,這使得方法調用非常明確.

返回值

你肯定也注意到任何方法都返回一個明確狀態的值.有時,會簡單到只要說明返回的類型(class)就行,但其他時候,你需要增加一個形容詞做前綴.

比如,(上述的)工廠方法以car開頭,它明確說明該方法返回一個Car類型的實例.(再比如)那兩個比較方法,fasterCar:與slowerCar:返回接受者以及參數中的faster/slower,API也會被描述的很清楚.但對于單類方法,遵守這種模式沒有任何意義(比如,sharedCar),因為這種規約的實例方法名本身就是含糊的.

更多關于命名的規約,請方法官方的Cocoa Coding Guidlines文檔

方法調用

正如在實例化與使用章節討論過地,你通過在方括號中放置用空格分開的對象以及所需方法來執行方法.參數與方法名用冒號分隔.

[porsche initWithModel:@"Porsche"];

當有多個參數時,則按照下面的方式,跟在初始參數之后.每個參數都有對應的標簽,并且需要用空格分隔,然后再跟上冒號:

[porsche initWithModel:@"Porsche" mileage:42000.0];

當你站在(方法)調用角度去看,就應該更容易理解上述命名規約的目的了.它們(命名規約)是的方法調用讀起來像是人類語言,而非機器語言.比如,比較下面OC與其他語言的類似的方法調用:

// Python/Java/C++
porsche.drive("Home", "Airport");

// Objective-C
[porsche driveFromOrigin:@"Home" toDestination:@"Airport"];

這可能會有更多的種類(冗長的方法名),所以Xcode 帶有如此好的自動補全功能.當你離開(遺忘)你的代碼,但是幾個月后再回來修復 bug 時,你就會對這種冗長(的命名)感激萬分.這種清晰(冗長的方法名)也使得使用第三方庫以及維護大型代碼庫變得更容易.

方法嵌套調用

嵌套調用方法是OC程序中常見的方式.把一個(方法)調用結果傳遞給另一個是很自然的.概念上來說,它們跟方法鏈(調用)完全一樣,只是方括號語法讓它們看起來有些不同罷了.

// JavaScript
Car.alloc().init()

// Objective-C
[[Car alloc] init];

[Car alloc]先被執行,然后init方法被其返回值調用.

受保護以及私有方法

對于OC方法來說,沒有受保護或者私有的訪問修飾符-它們都是公有的.然而,OC提供了一種可替代的組織范例來讓你實現等同的效果.

"私有"方法可以通過在實現文件中定義,但是在接口文件中省略來創建.因為其他對象(包括子類)永遠不準導入實現文件,所以,除了(定義這些方法的)類本身,這些方法有效地對其他對象隱藏了一切.

作為受保護方法的替代,OC提供了分類這種對隔離API更普遍的(大眾的)解決方案.我們會在Protected Methods中看到一個(關于分類的)完整的例子.

選擇器

選擇器是方法名在OC內部的表示.它們允許你將一個方法當做獨立的實體,從而你可以將(方法的)行為與需要執行(該行為)的對象分離.這是目標-行為設計模式的基礎,這在Ry's Cocoa TutorialInterface Builder章節中有介紹.它也是OC動態類型機制的組成部分.

可以通過兩種方式來得到方法名對應的選擇器.@selector()指令,可以將源代碼中的方法名轉成選擇器,而NSSelectorFromString()函數則可以將一個字符轉成選擇器(這個沒有前者高效).這兩種方式都返回一個稱作SEL的特殊數據類型.SEL的使用與BOOL,int或者其他數據類型完全一樣.

// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *porsche = [[Car alloc] init];
        porsche.model = @"Porsche 911 Carrera";
        
        SEL stepOne = NSSelectorFromString(@"startEngine");
        SEL stepTwo = @selector(driveForDistance:);
        SEL stepThree = @selector(turnByAngle:quickly:);
        
        // This is the same as:
        // [porsche startEngine];
        [porsche performSelector:stepOne];

        // This is the same as:
        // [porsche driveForDistance:[NSNumber numberWithDouble:5.7]];
        [porsche performSelector:stepTwo
                      withObject:[NSNumber numberWithDouble:5.7]];
        
        if ([porsche respondsToSelector:stepThree]) {
            // This is the same as:
            // [porsche turnByAngle:[NSNumber numberWithDouble:90.0]
            //              quickly:[NSNumber numberWithBool:YES]];
            [porsche performSelector:stepThree
                          withObject:[NSNumber numberWithDouble:90.0]
                          withObject:[NSNumber numberWithBool:YES]];
        }
        NSLog(@"Step one: %@", NSStringFromSelector(stepOne));
    }
    return 0;
}

任何對象都可以通過performSelector:以及(其他)相關的方法來執行選擇器.withObject:版本(方法)允許你給方法傳參數,但是這些參數必須是對象.如果這對你的需求有太多限制,你可以查看NSInvocation的高級用法.當你不確定目標對象是否已定義了這個方法,那你應該在嘗試執行選擇器之前使用respondsToSelector:進行檢查.

方法的術語名是串聯所有用冒號分隔參數標簽得到的基本方法名(The technical name for a method is the primary method name concatenated with all of its parameter labels, separated by colons.這句翻譯的太難受了,自己理解吧).讓冒號成為方法名的一部分可能會讓剛學OC的人感到迷惑.它們的用法可以歸納如下:無參方法永遠不包括冒號,而有參的方法永遠都是以冒號結尾.

下面是一個關于上述Car類的接口和實現的例子.請注意我們必須給performSelector:withObject:方法的參數傳遞NSNumber,而不是傳遞double(類型),因為它不允許你傳遞C語言的基本數據類型.

// Car.h
#import <Foundation/Foundation.h>

@interface Car : NSObject

@property (copy) NSString *model;

- (void)startEngine;
- (void)driveForDistance:(NSNumber *)theDistance;
- (void)turnByAngle:(NSNumber *)theAngle
            quickly:(NSNumber *)useParkingBrake;

@end
// Car.m
#import "Car.h"

@implementation Car

@synthesize model = _model;

- (void)startEngine {
    NSLog(@"Starting the %@'s engine", _model);
}

- (void)driveForDistance:(NSNumber *)theDistance {
    NSLog(@"The %@ just drove %0.1f miles",
          _model, [theDistance doubleValue]);
}

- (void)turnByAngle:(NSNumber *)theAngle
            quickly:(NSNumber *)useParkingBrake {
    if ([useParkingBrake boolValue]) {
        NSLog(@"The %@ is drifting around the corner!", _model);
    } else {
        NSLog(@"The %@ is making a gentle %0.1f degree turn",
              _model, [theAngle doubleValue]);
    }
}

@end

總結

這個模塊解釋了OC方法命名規約背后的原因,我們也知曉了OC方法中沒有訪問修飾符,知曉怎樣利用@selector去動態地執行方法.

適應一種新的規約會是一個痛苦的(難受的)過程,并且這種OC與其他OOP語言的戲劇性語法差異不會讓你覺得輕松( and the dramatic syntactic differences between Objective-C and other OOP languages won’t make your life any easier.又是憂傷的一句).不要強迫自己把OC語言加到你頭腦中已存在的編程模型世界中,而是要去理解內在的本質(Instead of forcing Objective-C into your existing mental model of the programming universe, it helps to approach it in its own right. 更憂傷了).嘗試設計一些簡單的程序之后,再給OC這種冗長的(設計)哲學下判斷下判斷.

那些(前些章節)涵蓋了OC面向對象編程的基礎(知識).這部教程的剩余內容會探討更牛逼的方式來組織代碼.首先列出的是協議,它允許在多個類之間共享一個API.


寫于15年09月07號

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,488評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,034評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,327評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,554評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,337評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,883評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,975評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,114評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,625評論 1 332
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,555評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,737評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,244評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,973評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,615評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,343評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,699評論 2 370

推薦閱讀更多精彩內容