美文网首页
Methods - 方法

Methods - 方法

作者: yiron | 来源:发表于2015-09-07 14:28 被阅读125次

    来源于 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号

    相关文章

      网友评论

          本文标题:Methods - 方法

          本文链接:https://www.haomeiwen.com/subject/wbrycttx.html