來源于 Ry’s Objective-C Tutorial - RyPress
一個(gè)學(xué)習(xí)Objective-C基礎(chǔ)知識(shí)的網(wǎng)站.
個(gè)人覺得很棒,所以決定抽時(shí)間把章節(jié)翻譯一下.
本人的英語水平有限,有讓大家誤解或者迷惑的地方還請(qǐng)指正.
原文地址:http://rypress.com/tutorials/objective-c/methods.html
僅供學(xué)習(xí),如有轉(zhuǎn)摘,請(qǐng)注明出處.
方法代表著一個(gè)對(duì)象如何執(zhí)行操作.它們與代表著對(duì)象數(shù)據(jù)的屬性在邏輯上等同.你可以把方法當(dāng)做附加給對(duì)象的函數(shù),但他們有著不同的語法.
在這個(gè)模塊,我們將探討OC方法的命名規(guī)約,(這些規(guī)約)對(duì)有著C++,Java,Python以及其他類似語言的開發(fā)經(jīng)驗(yàn)人員來說,會(huì)比較frustrating(難受).我們也會(huì)簡(jiǎn)要的討論OC的訪問修飾符(用來設(shè)置訪問權(quán)限),并學(xué)習(xí)怎樣使用selectors(選擇器)關(guān)聯(lián)方法.
命名規(guī)約
OC的方法旨在(被設(shè)計(jì)成)移除API中任何含糊之處,所以導(dǎo)致了方法名很冗長(zhǎng)(啰嗦的嚇人),但是不可否認(rèn)的是,描述性很強(qiáng).遵守以下三條規(guī)則以實(shí)現(xiàn)OC方法的命名.
1.不要使用縮寫
2.明確規(guī)定方法自身的參數(shù)名
3.明確描述方法的返回值
在你閱讀下面的Car類接口時(shí)把這些規(guī)則記在心里.
// 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
縮寫
讓方法能夠被理解,被預(yù)料(可以被猜測(cè))的最簡(jiǎn)單方式就是避免縮寫.大多數(shù)OC編程人員都希望方法能被完全寫出,這是所有標(biāo)準(zhǔn)框架里的規(guī)約,從[Foundation](https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/ _classic/_index.html)到UIKit都是這樣.這就是上述接口中選擇使用maximumSpeed取代更簡(jiǎn)寫的maxSpeed的原因.
參數(shù)
另一個(gè)清楚(反映)OC冗長(zhǎng)設(shè)計(jì)哲學(xué)的例子就是方法參數(shù)的命名規(guī)約.C++,Java以及其他類似風(fēng)格的語言中,將參數(shù)與方法分離成單獨(dú)的實(shí)體,而OC的方法名實(shí)則包括它所有的參數(shù)名.
舉個(gè)例子,在C++中為了讓Car(方向)轉(zhuǎn)90°,你也許會(huì)使用這樣turn(90)的方法.但是,OC覺著這太含糊了.搞不清楚turn()使用什么樣的參數(shù)-可能是一個(gè)新的方向角,也可能是在調(diào)整(增加)你當(dāng)前的方向角.OC方法通過一個(gè)介詞來描述讓它明確.這種方式實(shí)現(xiàn)的API保證了方法永遠(yuǎn)不會(huì)被誤用:它(方法)要么是turnByAngle:90.要么是turnToAngle:90.
當(dāng)一個(gè)方法可接受多個(gè)參數(shù)時(shí),那么每個(gè)參數(shù)名都被包含在方法名中.上面的例子中,initWithModel:mileage:方法把Model和mileage都帶上了.正如等會(huì)我們所見,這使得方法調(diào)用非常明確.
返回值
你肯定也注意到任何方法都返回一個(gè)明確狀態(tài)的值.有時(shí),會(huì)簡(jiǎn)單到只要說明返回的類型(class)就行,但其他時(shí)候,你需要增加一個(gè)形容詞做前綴.
比如,(上述的)工廠方法以car開頭,它明確說明該方法返回一個(gè)Car類型的實(shí)例.(再比如)那兩個(gè)比較方法,fasterCar:與slowerCar:返回接受者以及參數(shù)中的faster/slower,API也會(huì)被描述的很清楚.但對(duì)于單類方法,遵守這種模式?jīng)]有任何意義(比如,sharedCar),因?yàn)檫@種規(guī)約的實(shí)例方法名本身就是含糊的.
更多關(guān)于命名的規(guī)約,請(qǐng)方法官方的Cocoa Coding Guidlines文檔
方法調(diào)用
正如在實(shí)例化與使用章節(jié)討論過地,你通過在方括號(hào)中放置用空格分開的對(duì)象以及所需方法來執(zhí)行方法.參數(shù)與方法名用冒號(hào)分隔.
[porsche initWithModel:@"Porsche"];
當(dāng)有多個(gè)參數(shù)時(shí),則按照下面的方式,跟在初始參數(shù)之后.每個(gè)參數(shù)都有對(duì)應(yīng)的標(biāo)簽,并且需要用空格分隔,然后再跟上冒號(hào):
[porsche initWithModel:@"Porsche" mileage:42000.0];
當(dāng)你站在(方法)調(diào)用角度去看,就應(yīng)該更容易理解上述命名規(guī)約的目的了.它們(命名規(guī)約)是的方法調(diào)用讀起來像是人類語言,而非機(jī)器語言.比如,比較下面OC與其他語言的類似的方法調(diào)用:
// Python/Java/C++
porsche.drive("Home", "Airport");
// Objective-C
[porsche driveFromOrigin:@"Home" toDestination:@"Airport"];
這可能會(huì)有更多的種類(冗長(zhǎng)的方法名),所以Xcode 帶有如此好的自動(dòng)補(bǔ)全功能.當(dāng)你離開(遺忘)你的代碼,但是幾個(gè)月后再回來修復(fù) bug 時(shí),你就會(huì)對(duì)這種冗長(zhǎng)(的命名)感激萬分.這種清晰(冗長(zhǎng)的方法名)也使得使用第三方庫(kù)以及維護(hù)大型代碼庫(kù)變得更容易.
方法嵌套調(diào)用
嵌套調(diào)用方法是OC程序中常見的方式.把一個(gè)(方法)調(diào)用結(jié)果傳遞給另一個(gè)是很自然的.概念上來說,它們跟方法鏈(調(diào)用)完全一樣,只是方括號(hào)語法讓它們看起來有些不同罷了.
// JavaScript
Car.alloc().init()
// Objective-C
[[Car alloc] init];
[Car alloc]先被執(zhí)行,然后init方法被其返回值調(diào)用.
受保護(hù)以及私有方法
對(duì)于OC方法來說,沒有受保護(hù)或者私有的訪問修飾符-它們都是公有的.然而,OC提供了一種可替代的組織范例來讓你實(shí)現(xiàn)等同的效果.
"私有"方法可以通過在實(shí)現(xiàn)文件中定義,但是在接口文件中省略來創(chuàng)建.因?yàn)槠渌麑?duì)象(包括子類)永遠(yuǎn)不準(zhǔn)導(dǎo)入實(shí)現(xiàn)文件,所以,除了(定義這些方法的)類本身,這些方法有效地對(duì)其他對(duì)象隱藏了一切.
作為受保護(hù)方法的替代,OC提供了分類這種對(duì)隔離API更普遍的(大眾的)解決方案.我們會(huì)在Protected Methods中看到一個(gè)(關(guān)于分類的)完整的例子.
選擇器
選擇器是方法名在OC內(nèi)部的表示.它們?cè)试S你將一個(gè)方法當(dāng)做獨(dú)立的實(shí)體,從而你可以將(方法的)行為與需要執(zhí)行(該行為)的對(duì)象分離.這是目標(biāo)-行為設(shè)計(jì)模式的基礎(chǔ),這在Ry's Cocoa Tutorial的Interface Builder章節(jié)中有介紹.它也是OC動(dòng)態(tài)類型機(jī)制的組成部分.
可以通過兩種方式來得到方法名對(duì)應(yīng)的選擇器.@selector()指令,可以將源代碼中的方法名轉(zhuǎn)成選擇器,而NSSelectorFromString()函數(shù)則可以將一個(gè)字符轉(zhuǎn)成選擇器(這個(gè)沒有前者高效).這兩種方式都返回一個(gè)稱作SEL的特殊數(shù)據(jù)類型.SEL的使用與BOOL,int或者其他數(shù)據(jù)類型完全一樣.
// 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;
}
任何對(duì)象都可以通過performSelector:以及(其他)相關(guān)的方法來執(zhí)行選擇器.withObject:版本(方法)允許你給方法傳參數(shù),但是這些參數(shù)必須是對(duì)象.如果這對(duì)你的需求有太多限制,你可以查看NSInvocation的高級(jí)用法.當(dāng)你不確定目標(biāo)對(duì)象是否已定義了這個(gè)方法,那你應(yīng)該在嘗試執(zhí)行選擇器之前使用respondsToSelector:進(jìn)行檢查.
方法的術(shù)語名是串聯(lián)所有用冒號(hào)分隔參數(shù)標(biāo)簽得到的基本方法名(The technical name for a method is the primary method name concatenated with all of its parameter labels, separated by colons.這句翻譯的太難受了,自己理解吧).讓冒號(hào)成為方法名的一部分可能會(huì)讓剛學(xué)OC的人感到迷惑.它們的用法可以歸納如下:無參方法永遠(yuǎn)不包括冒號(hào),而有參的方法永遠(yuǎn)都是以冒號(hào)結(jié)尾.
下面是一個(gè)關(guān)于上述Car類的接口和實(shí)現(xiàn)的例子.請(qǐng)注意我們必須給performSelector:withObject:方法的參數(shù)傳遞NSNumber,而不是傳遞double(類型),因?yàn)樗辉试S你傳遞C語言的基本數(shù)據(jù)類型.
// 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
總結(jié)
這個(gè)模塊解釋了OC方法命名規(guī)約背后的原因,我們也知曉了OC方法中沒有訪問修飾符,知曉怎樣利用@selector去動(dòng)態(tài)地執(zhí)行方法.
適應(yīng)一種新的規(guī)約會(huì)是一個(gè)痛苦的(難受的)過程,并且這種OC與其他OOP語言的戲劇性語法差異不會(huì)讓你覺得輕松( and the dramatic syntactic differences between Objective-C and other OOP languages won’t make your life any easier.又是憂傷的一句).不要強(qiáng)迫自己把OC語言加到你頭腦中已存在的編程模型世界中,而是要去理解內(nèi)在的本質(zhì)(Instead of forcing Objective-C into your existing mental model of the programming universe, it helps to approach it in its own right. 更憂傷了).嘗試設(shè)計(jì)一些簡(jiǎn)單的程序之后,再給OC這種冗長(zhǎng)的(設(shè)計(jì))哲學(xué)下判斷下判斷.
那些(前些章節(jié))涵蓋了OC面向?qū)ο缶幊痰幕A(chǔ)(知識(shí)).這部教程的剩余內(nèi)容會(huì)探討更牛逼的方式來組織代碼.首先列出的是協(xié)議,它允許在多個(gè)類之間共享一個(gè)API.
寫于15年09月07號(hào)