來源于 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 Tutorial的Interface 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號