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阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容