MKMapKit引发的对命名规范的讨论

MKMapKit命名规范

作为苹果官方的framework,我觉得它在变量命名和使用上有很多值得我们借鉴的地方

首先从最简单的部分看起:MKAnnotation.h

@protocol MKAnnotation <NSObject>

// Center latitude and longitude of the annotation view.
// The implementation of this property must be KVO compliant.
@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;

@optional

// Title and subtitle for use by selection UI.
@property (nonatomic, readonly, copy) NSString *title;
@property (nonatomic, readonly, copy) NSString *subtitle;

// Called as a result of dragging an annotation view.
- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate NS_AVAILABLE(10_9, 4_0);

@end

这个文件十分简单,从中我们只关注几个细节:

  1. 对于@protocol @optional这些关键字做了上下空行处理,利于我们注意到到这些声明位置。
  2. 对于属性访问权限的控制,上述代码中将coordinate的读写方法用readonly关键字和setter方法进行分离,我觉得这可能是一个粗心的开发者在设计接口时常常忽略的细节。
  3. 对于属性的自定义setter方法使用new+属性名作为参数名,这是一个细节,我们之后讨论。
  4. 对于NSString型属性,统一使用了copy关键字修饰,通常这是为了安全考虑。究其原因,因为NSString 为 NSMutableString 的基类,如果将NSMutableString 以retain的形式赋值给NSString后,后续修改NSMutableString会导致NSString内容的变化,这通常不是我们希望的,所以用copy最安全。同理对NSString,NSSet之类也有类似处理

下面来看一个稍长一些的头文件:MKAnnotationView.h

其中我选取了部分代码

// Post this notification to re-query callout information.
MK_EXTERN NSString * const MKAnnotationCalloutInfoDidChangeNotification;

typedef NS_ENUM(NSUInteger, MKAnnotationViewDragState) {
    MKAnnotationViewDragStateNone = 0,      // View is at rest, sitting on the map.
    MKAnnotationViewDragStateStarting,      // View is beginning to drag (e.g. pin lift)
    MKAnnotationViewDragStateDragging,      // View is dragging ("lift" animations are complete)
    MKAnnotationViewDragStateCanceling,     // View was not dragged and should return to its starting position (e.g. pin drop)
    MKAnnotationViewDragStateEnding         // View was dragged, new coordinate is set and view should return to resting position (e.g. pin drop)
} NS_ENUM_AVAILABLE(10_9, 4_0);

对于这两个变量的命名我们可以看到都是把'MKAnnotationView'完整的放在变量名字前面,通常他们作为可以被外部引用的类型,这样命名有助于清楚的表示出他的从属和含义。

在这部分代码中,我们看到了两个我们经常使用的数据定义方式:
用于包裹通知的字符串

  1. 常量字符串对象: MKAnnotationCalloutInfoDidChangeNotification是一个指向NSString对象的常量指针,通常这种类型是用于包装通知的。其实涉及到的const的使用就不赘述了。

  2. 其中有个很有趣的东西:MK_EXTERN。我们来看看定义:

    #ifdef __cplusplus
    #define MK_EXTERN       extern "C" __attribute__((visibility ("default")))
    #else
    #define MK_EXTERN       extern __attribute__((visibility ("default")))
    #endif
    

    这个说起来也挺长的,注意几点即可: 一.cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码。 二.被extern限定的函数或变量是extern类型的 三.在cpp代码中使用extern “C"表示这一点以C的方式处理函数名。四.attribute__((visibility (“default”))会使链接过程中符号在所有情况下都被输出,使得这些符号都对外部可见,以用来让外部文件也能使用和操作他。

枚举型

  1. 说到枚举型变量,我们来看看系统关于NS_ENUM的定义

    #define NS_ENUM(_type, _name) CF_ENUM(_type, _name)
    #define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)
    // Enums and Options
    #if (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && __has_feature(objc_fixed_enum))
    #define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
    #if (__cplusplus)
    #define CF_OPTIONS(_type, _name) _type _name; enum : _type
    #else
    #define CF_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
    #endif
    #else
    #define CF_ENUM(_type, _name) _type _name; enum
    #define CF_OPTIONS(_type, _name) _type _name; enum
    #endif
    

    我们可以注意到NS_ENUM就是对emun和typedef关键字的封装,这里需要提的一个问题是关于NS_ENUM和NS_OPTIONS的一些小区别。从枚举定义来看,NS_ENUM和NS_OPTIONS本质是一样的,仅仅从字面上来区分其用途。NS_ENUM是通用情况,NS_OPTIONS一般用来定义具有位移操作或特点的情况(bitmask)。
    比如在MKDirectionsTypes.h文件中:

    typedef NS_OPTIONS(NSUInteger, MKDirectionsTransportType) {
    MKDirectionsTransportTypeAutomobile     = 1 << 0,
    MKDirectionsTransportTypeWalking        = 1 << 1,
    MKDirectionsTransportTypeAny            = 0x0FFFFFFF
    } NS_ENUM_AVAILABLE(10_9, 7_0);
    
  2. 对于枚举型变量,我们还注意到一点他们是名词的组合,对于start, drag, walk这类有动词含义的词都已ing形式命名,诸如:starting,dragging,walking。

我们再看另一部分

// Defaults to YES. If NO, ignores touch events and subclasses may draw differently.
@property (nonatomic, getter=isEnabled) BOOL enabled;

// Defaults to NO. This gets set/cleared automatically when touch enters/exits during tracking and cleared on up.
@property (nonatomic, getter=isHighlighted) BOOL highlighted;

// Defaults to NO. Becomes YES when tapped/clicked on in the map view.
@property (nonatomic, getter=isSelected) BOOL selected;
- (void)setSelected:(BOOL)selected animated:(BOOL)animated;

对于BOOL型变量,这里有个很有趣的处理方式:

  1. BOOL变量名是动词的过去式,诸如animated, selected
  2. 都重命名了getter方法:以is开头诸如isEnabled, isHighlighted

还有一个细节

对于init方法:

- (instancetype)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier;

我们使用instancetype作为返回值类型,使那些非关联返回类型的方法返回所在类的类型。也就是说在这里我们可以明确的知道这个函数返回的类型就是MKAnnotationView。

继续往后看我们感兴趣的 在MKDirections.h中

typedef void (^MKDirectionsHandler)(MKDirectionsResponse *response, NSError *error);
typedef void (^MKETAHandler)(MKETAResponse *response, NSError *error);

这里有两个block类型的定义, 看看细节:

  1. 和其他能被全局引用的变量一样以相关的类名描述作为变量名前缀部分(MKDirections, MKETA), 以Handler标记他为block类型。
  2. 作为请求结果的回调,参数为response和error。

下面我们聚焦到MKGeometry.h

这个文件除了最下面的 ‘NSValue (NSValueMapKitGeometryExtensions)’ 之外都是都像是C语言的头文件,它主要是封装了很多对于地图常用的数据结构和一些计算函数。

挑几个有代表性的

typedef struct {
    CLLocationDegrees latitudeDelta;
    CLLocationDegrees longitudeDelta;
} MKCoordinateSpan;

NS_INLINE MKMapRect MKMapRectMake(double x, double y, double width, double height) {
    return (MKMapRect){ MKMapPointMake(x, y), MKMapSizeMake(width, height) };
}

NS_INLINE double MKMapRectGetMinY(MKMapRect rect) {
    return rect.origin.y;
}

MK_EXTERN MKMapRect MKMapRectInset(MKMapRect rect, double dx, double dy) NS_AVAILABLE(10_9, 4_0);

我们可以看到一个struct,两个内联函数定义,一个extern函数的声明。

  1. 同前面看到的情况类似,对于全局可以调用的数据类型,在这里是struct,内联函数,extern函数都需要添加类前缀。
  2. 在这里我们注意到一个细节,看到了两个名词的缩写 rect和min。 这里我们把这个讨论放到之后。
  3. 还有一个细节是关于get和make这一类动词的使用, 在之后我们也会进行一些整理。

最后再看一下MKMapView.h这个文件的一些东西

这个文件对于开发者来说应该是最常用引用的一个头文件了,其实它相对而言是非常简单易懂的。本文不谈功能和业务。从这个文件来看一些命名的细节。

@property (nonatomic) BOOL showsUserLocation;

- (void)removeAnnotations:(NSArray *)annotations;
  1. show这个动词用了第三人称的现在时态。
  2. 对于NSArray类型的变量用到了名词的复数形式。

我们小小的总结一下

事实上,上述只是零零散散说了一些细节。作为一篇想把命名方式说的比较清楚的文章,明显这样做是不负责任的。 但是通过上述的一些描述有助于我们更好的理解下面的分析。

下面进入我们真正的主题,如何让我们的代码看起来更有"Apple Style”

这里吹个牛逼引入了"Apple Style"这个词, 作为一个不牛逼iOS开发者,我觉得苹果他们自己家的命名还是非常棒的。

下面我们开始吧

在前面第一部分的文章中我们着重强调了一个概念:

类前缀

何时需要呢?在此我们偷个懒看一下苹果官方文档的解释:

Use prefixes when naming classes, protocols, functions, constants, and typedef structures. Do not use prefixes when naming methods; methods exist in a name space created by the class that defines them. Also, don’t use prefixes for naming the fields of a structure.

当我们定义Class,Protocal,functions(这里很有意思,functions我们认为是C语言全局函数, 而methods是objective-c的函数),Constant(全局的常量)以及typedef一个struct的时候。举个例子:

1. @interface MKMapView : UIView <NSCoding>
2. @protocol MKAnnotation <NSObject>
3. MK_EXTERN NSString * const MKAnnotationCalloutInfoDidChangeNotification;
4. typedef struct {
    CLLocationDegrees latitudeDelta;
    CLLocationDegrees longitudeDelta;
} MKCoordinateSpan;

补充一点,枚举型也需要添加前缀

typedef NS_OPTIONS(NSUInteger, MKDirectionsTransportType) {
  MKDirectionsTransportTypeAutomobile     = 1 << 0,
  MKDirectionsTransportTypeWalking        = 1 << 1,
  MKDirectionsTransportTypeAny            = 0x0FFFFFFF
} NS_ENUM_AVAILABLE(10_9, 7_0);

何时不需要?
翻译上述后半段,

  1. 对于oc的方法都不要加前缀因为他们的namespace已经被他们从属的类定义过了。

  2. 不要对struct内部的变量名加前缀。

1.- (instancetype)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier;

2.typedef struct {
    CLLocationDegrees latitudeDelta;
    CLLocationDegrees longitudeDelta;
} MKCoordinateSpan;

嗯, 在真正开始分析变量和函数命名之前

我们有必要复习一下与我们主题相关的英语语法和词法

本人英语水平有限,下面都是我通过现查总结的:

列举一下我们常见的词性:

  1. 名词(n):这个最好理解了,指代人、物、事、时、地、情感、概念等实体或抽象事物的词。 在这里比如map, annotation, error等等
  2. 动词(v):动词是用来表示各类动作的词汇。比如get,make等等。
  3. 形容词(adj):形容词是很多语言中均有的主要词类中的一种。形容词用来修饰名词或代词,表示人或事物的性质,状态,和特征。比如:max,min。
  4. 介词(prep):介词是一种用来表示词与词、词与句之间的关系的虚词, 诸如 at,in,before,after,on,by,with等。
  5. 分词(participle):具有动词及形容词二者特征的词;尤指以ing、ed、d、t、en或n结尾的英语动词性形容词,具有形容词功能,同时又表现各种动词性特点,如时态,语态、带状语性修饰语的性能及带宾词的性能。
  6. 动词的扩展:情态动词:should,need等。 动名词:V+ing等

再看一下句子成分:

  1. 主语(subject)是句子陈述的对象,说明是谁或是什么
  2. 谓语(predicate)说明主语所发出的动作或具有的特征或状态,一般由动词来承担。
  3. 宾语(object),又称受词,是指一个动作(动词)的对象或接受者,常位于及物动词或介词后面。
  4. 表语(predicative)是用来说明主语的身份、性质、品性、特征和状态的,表语常由名词、形容词、副词、介词短语、不定式、动词的-ing、从句来充当,它常位于系动词(be, become, appear, seem, look ,sound, feel, get, smell等词)之后。

来看主谓宾组合的句子
比如 I need you就是最最简单的主谓宾短句。

现在我们要着重介绍一个概念叫做宾语从句,在复合句中,由一个句子充当宾语,这个句子就叫做宾语从句。
比如: He is wondering when can he finish this difficult job. 'when can he finish this difficult job'就是一个句子起到了宾语的作用

还有一个是主系表
例如The problem is puzzling.
在重点看一个句子:The problem is when we can get a pay rise. ‘when we can get a pay rise’就是整句话作为表语

这两个句子很容易让我们联想到OC的方法, 如果调用者是主语的话,如果方法的目的是去做一件事,就像是主谓宾句式中的'谓语+宾语'部分。如果方法是去得到一个状态或者属性,方法本身又是起到"系动词+表语"的作用。

我们来看变量名

我们首先来看oc类中的变量,或者说属性(property)。 因为他代表的就是所属类的特征, 通常来说在命名上不需要明确的指出他从属关系,正常来说他们就是一些简单的名词动词形容词,或者说是相关的词组。

首先苹果默认的私有变量前加了一个下划线,我们通常不希望打破这个约定,所以一般这么做

@implementation MyClass {
    BOOL _showsTitle;
}

下面这部分我们来看看到底如何起名字比较好:

首先也是最重要的一点是:明确

我们来看一些例子

在MKMapItem.h中:

@property (nonatomic, readonly) BOOL isCurrentLocation;
@property (nonatomic, copy) NSString *phoneNumber;

明确的意思是:用词准确清晰。 看上述给了我们很少的误解,可能你觉得我举得例子很牵强,那我们改换一种写法

@property (nonatomic, readonly) BOOL isCurrLocation;
@property (nonatomic, copy) NSString *phone;

看起来应该没有上面的舒服吧,很多时候造成命名不明确的就是我们喜欢缩写和省略词组的一部分。

使用缩写

那么问题来了,我们真的不能缩写了吗?! 苹果给了一个说法是这样的,有的时候用一下大家都明白的缩写也是可以滴。
为此,我们在文档中找到了一段神奇的内容:
MKMapKit引发的对命名规范的讨论_第1张图片
我们多希望它能长一点呀,这样我们写代码的时候就可以少敲点键盘了。

作为一名机智的程序员, 我觉得可以扩充一下它。为此我们再去翻看苹果的代码。

在MKPolygon.h中我们看到一段:

+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count;

注意到"coords"没有, 他就是coordinates的缩写。那么….问题又来了,到底何时用比较好,这个"度"在哪呢?

让我们尝试着总结一下:

  1. 缩写不会和别的词汇产生混淆和冲突, 假设我们把Matrix简写成mtx就很容易和其他词(比如max,mix)产生混淆。
  2. 这个要足够常用,上述代码中的coords出现在各种overlay的子类中。
  3. 我们需要保持统一,也就是一旦采用coords这种写法,就不要在类似的场合用coordinates。

避免过长命名

随着我们分析到缩写和省略词组的一部分会造成困扰。他的反向的极端,也就是说命名的太罗嗦和用到很多无意义的词汇也是不好的。

我们举个例子吧

@property (nonatomic, copy) NSString *phoneNumberString;
@property (nonatomic, strong) NSArray *coordinateArray;

其实这一部分,我暂时也没有想到什么特别好的例子。上述的两行代码看起来命名不会对使用者造成什么困扰。 但是很多时候我们也会觉得他过于啰嗦,究其原因就是变量名的后半部分指代了他的类型。

我们尝试去找一个合理的解释。在一篇文章中找到了一段描述。
MKMapKit引发的对命名规范的讨论_第2张图片

从中我们认为这是一个约定。

  1. 对于NSString, NSArray, NSNumber, BOOL类型我们无需指定类型
  2. 对于其他类型,比如Image, Indicator这样的UI组件在变量命名的后半部分指定它的类型是有必要的。 尤其对于XXXManager类型的变量写成比如fontManager是必须的,否则无法理解它的含义。
  3. 对于命名一个复数形式的变量,如果它不是NSArray或者NSSet最好指定类型。

准确表达词义

上面说了过短和过长的不利影响,这些很容易被我们发现和改正, 下面我们谈一谈如何能准确的表达词义。

这一部分很难总结出什么规律, 它的好坏的决定因素在于,开发者自己对于业务理解的准确和专业名词(或者说英文水平的积累)。但是, 我们仍然要迎难而上。

下面我们会分三个维度来分析这个问题 词义, 词性, 词组

首先我们来看看,MKMapKit中对于词汇的使用, 看代码:

@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (strong) UIColor *strokeColor;
@property CGLineJoin lineJoin; // defaults to kCGLineJoinRound

@property (nonatomic, readonly, getter=isLoading) BOOL loading;
@property (nonatomic, getter=isSelected) BOOL selected;

@property (nonatomic, getter=isZoomEnabled) BOOL zoomEnabled;
@property (nonatomic) BOOL canShowCallout;
@property (nonatomic, getter=isDraggable) BOOL draggable NS_AVAILABLE(10_9, 4_0);

@property (strong, nonatomic) UIView *leftCalloutAccessoryView;

词义:

这部分取决于我们的经验和词汇积累,如果我们熟悉地理相关的开发我们就知道coordinate表示的是坐标, 而熟悉绘图框架的人就知道strokeColor表示的是画线的颜色。

词性:

在这里 变量一般表示三个意思:1.组成部分 2.状态。3.权限。

  1. 对于组成部分而言一般来说是一个名词,诸如上面的leftCalloutAccessoryView(事实上他是一个词组,我们稍后解释)
  2. 表示状态的一般是动词+时态,比如上述的loading和selected。有趣的是,他们的getter方法都写成了 is+变量名,这样他真正被用起来的时候就更加自然,这个我们在分析函数命名的时候再看
  3. 对于权限的描述在这里有几种写法 动词+enabled, 形容词(draggable), 情态动词+动作(canShowCallout)。

这里看到的东西其实很多脱离了词性的范畴, 看起来像是句子了, 在这里先不展开。

词组:

我们引入一个概念叫做偏正词组,它是由修饰语和中心语组成,结构成分之间有修饰与被修饰关系的词组,再引入一个他的分支:定中词组。 定中词组的修饰语是定语,充当中心语的一般是体词性成分,定语从领属、范围、质料、形式、性质、数量、用途、时间、处所等方面描写或限制中心语。我们来看leftCalloutAccessoryView, 这里的AccessoryView就从属于leftCallout。

需要避免的用词

很多时候 我们喜欢用一些指代不明的词汇。下面例举一些:

  1. object
  2. data
  3. flag

当这些单词单独出现在我们的程序当中时,常常让人手足无措。

说到这里,对于我们去命名变量的借鉴意义到底在哪呢。

  1. 我们在动手写之前先去找到合适的词是命名我们的变量
  2. 我们在命名变量的时候 不使用介词, 就是说我们会说 A's B 而不是 B of A。这样的约定下从属关系是一目了然的。 如果leftCalloutAccessoryView换成accessoryViewOfLeftCallout看起来就有点绕了。

先告别变量命名, 我们来看函数命名

有了之前对于内容的铺垫, 我们对程序中最小的单位变量在命名上有了一些参考。下面来看如何来命名一个合适的函数将他们组合起来。

有一点需要提的 objective-c是一门真真贴近自然语言的变成语言。所以当我们思考函数命名的时候考虑一下简单的翻译成句子是不是通畅是一个很重要的衡量标准。

以名词开头

来看一个简单的例子

- (MKAnnotationView *)viewForAnnotation:(id <MKAnnotation>)annotation;

这里我们看到MKMapView的一个方法,虽然他很短,我们也拆开来分析它。viewForAnnotation这一段由三个单词组成 view(名词), for(介词), annotation(名词)。 他们构成了一个介词词组,简单的翻译介词的意思就构成了 'annotation所对应的view',再加上这个方法的调用着,我们称它为mapView吧。他们共同构成了这样一个句子 ‘mapView上的一个annotation对应的view’。

我们再看它为何如此排列。在调用他的时候

[mapView viewForAnnotation:annotation]

返回值类型view写在开头,这能让我们很清楚的看到这个方法的返回值。参数写在单词annotation之后,标记他为一个annotation相关类型的参数。一切如此自然。在这里我们把这一类方法归为以名词开头

以动词(原型)开头的方法

- (void)addOverlay:(id <MKOverlay>)overlay level:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);
- (NSArray *)overlaysInLevel:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);

- (void)insertOverlay:(id <MKOverlay>)overlay atIndex:(NSUInteger)index level:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);

第一个方法以动词开头,返回值为void。 分析一下他的组成 add(动词) + overlay(名词) + level(名词)。根据英文语法分析,这句话是不对的啊,在看下面第二个,用到了in level, 第三个函数对于index前也加了介词at。 目前看来这对我们分析出结构产生了不好的影响, 为此我专门查了一下level本身有一个动词含义是 '对准'。任性的apple工程师啊。。。

还有一种情况,我们引入NSFileManager.h文件:

- (BOOL)createSymbolicLinkAtURL:(NSURL *)url withDestinationURL:(NSURL *)destURL error:(NSError **)error NS_AVAILABLE(10_7, 5_0);

这个也是以动词开头,但是以BOOL作为返回值。

下面我们开始总结这样一种命名方式:

  1. 对于以动词开头的函数,表示去执行某一个任务。当我们把调用者添加到函数最前面的时候能够发现他是一个可读的语句。
  2. 我们一般定义他的返回值为void, 当需要得到他是否执行成功的状态时可以以BOOL作为返回值

以动词第三人称开头

说到这里,有一个上面遗留的问题,以动词第三人称单数形式开头,看代码

@property (nonatomic) BOOL showsPointsOfInterest NS_AVAILABLE(10_9, 7_0);
@property (nonatomic) BOOL showsUserLocation;

对于这两个属性,我们更愿意理解他们为方法名而不是变量名。从功能上看他们的getter方法返回的是bool型,也就是返回一个状态。于是我们尝试着从语言的角度分析一下。 'mapView get whether it shows userLocation.',类似表语从句结构。 从这里看我们认为以动词第三人称单数开头方式开头的一般作为返回BOOL的命名方式。

前面提过对于返回BOOL型有几种写法:

  1. 动词+时态,比如loading和selected
  2. 形容词 比如draggable
  3. 动词+形容词 表达的是形容词含义 比如zoomEnabled
  4. 情态动词+动作 比如canShowCallout

对于前三种表示形容词含义的, 如果加上调用者那个名词凑成短句, 在前面加上is会非常清楚,比如: [mapView isZoomEnabled];

对于动词第三人称开头或者情态动词(can, should)开头则不需要这么做。

有一点需要注意的是,不要使用do,does一类无含义的情态动词

讨论完bool变量作为返回值的情况之后,我们来看看如果方法变量为一个bool值会如何命名:

- (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated;

回头看比如对形容词draggable或者从句canShowCallout这类setter方法我们无需讨论了。

就看这个animated,回到我们的编程命名习惯上,通常animate是当动词使用的,而animating一般表示正在进行中。在这里需要注意的是 animated在这里是作为形容词 释义为:“需要以动画方式进行”。

在苹果文档中,有一个原则 当使用分词的时候把它当做动词写到他修饰的词前面,而不要使用它的形容词形态放到后面
例子是这样的

- (BOOL)acceptsGlyphInfo; -------->Right
- (BOOL)glyphInfoAccepted; ------->Wrong

方法中的一些时态问题

在oc的各种回调函数中 我们最常见的两个词是will和did,一个表示将要,一个表示已完成。这部分在回调函数中会经常出现。

方法中介词的使用

前面我们说到: 介词(prep):介词是一种用来表示词与词、词与句之间的关系的虚词, 诸如 at,in,before,after,on,by,with等。

我们尝试着总结一下常用的介词和介词词组的使用

- (MKMapPoint)mapPointForPoint:(CGPoint)point NS_DEPRECATED_IOS(4_0, 7_0);

- (void)drawMapRect:(MKMapRect)mapRect
          zoomScale:(MKZoomScale)zoomScale
          inContext:(CGContextRef)context NS_DEPRECATED_IOS(4_0, 7_0);

+ (instancetype)circleWithCenterCoordinate:(CLLocationCoordinate2D)coord
                                  radius:(CLLocationDistance)radius;

- (instancetype)initWithCircle:(MKCircle *)circle;

- (CLLocationDistance)distanceFromString:(NSString *)distance;

+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
                              fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
                                    eyeAltitude:(CLLocationDistance)eyeAltitude;

- (void)applyStrokePropertiesToContext:(CGContextRef)context
                           atZoomScale:(MKZoomScale)zoomScale;

- (void)fillPath:(CGPathRef)path inContext:(CGContextRef)context;

- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view;

首先我们发现我们发现在多个参数并列没有用到and这类连词,and这类连词并不能对我理解代码产生任何帮助

我们总结了一些介词和相关词组

  1. for
  2. draw in
  3. with
  4. init with
  5. from
  6. look at from
  7. apply to at
  8. fill in
  9. convert to

在实际的编程中, 常常忽略这些词的合适场景, 继而造成别人理解上的障碍。

来看 for与from他们都有类型转换的意思,在这里它们表示的都是"从。。。而来”。观察一些代码:

- (CGPoint)pointForMapPoint:(MKMapPoint)mapPoint;
- (MKMapPoint)mapPointForPoint:(CGPoint)point;
- (CGRect)rectForMapRect:(MKMapRect)mapRect;
- (MKMapRect)mapRectForRect:(CGRect)rect;
- (CGPoint)pointForCoordinate:(CLLocationCoordinate2D)coordinate;

- (NSString *)stringFromDistance:(CLLocationDistance)distance;
- (CLLocationDistance)distanceFromString:(NSString *)distance;
// NSDateFormatter.h
- (NSString *)stringFromDate:(NSDate *)date;
- (NSDate *)dateFromString:(NSString *)string;

我们发现for一般用在坐标类型的转换函数中(他们格式类似,数值不同),而from多用在其他的类型转换上

再看看in,at

- (NSArray *)overlaysInLevel:(MKOverlayLevel)level NS_AVAILABLE(10_9, 7_0);

- (void)applyStrokePropertiesToContext:(CGContextRef)context
                           atZoomScale:(MKZoomScale)zoomScale;

- (void)insertOverlay:(id <MKOverlay>)overlay atIndex:(NSUInteger)index NS_AVAILABLE(10_9, 4_0);

- (void)drawMapRect:(MKMapRect)mapRect
          zoomScale:(MKZoomScale)zoomScale
          inContext:(CGContextRef)context NS_DEPRECATED_IOS(4_0, 7_0);

这几个介词通常取决于他之后的单词, 这里记住几个词组吧。in level, in context, at zoom scale, at index。

再来看

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id <MKOverlay>)overlay;
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id <MKOverlay>)overlay

for有"对于"的意思,所以两个对象有比较复杂关系的时候,通常用for将他们连接

再有

- (CGPoint)convertCoordinate:(CLLocationCoordinate2D)coordinate toPointToView:(UIView *)view;
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view;
- (CGRect)convertRegion:(MKCoordinateRegion)region toRectToView:(UIView *)view;
- (MKCoordinateRegion)convertRect:(CGRect)rect toRegionFromView:(UIView *)view;

这部分之前打算放到"细节和例外情况"中去的,它们都以动词开头,但是都是有返回值的。 convert to这个词组表示一个转换动作, 他最后的from/to修饰前面第一个和第二个名词, 其实这个方法一度对我也造成了困扰。

最后看看with,这个词我们经常会看到,通常它代表的含义是"跟 … 一起”, 我们经常见到的init with, 包括MKCricle的 cricleWith…等。

回调函数

此处引用苹果文档内容

回调有自己独特的结构

  1. 他的第一个参数是消息的发送者

    - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
    - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;```
    
  2. 冒号的位置应该放在委托对象之后,除非只有一个参数

    我们不该

    - (BOOL)application:(NSApplication *)sender OpenUntitledFile;
    

    而应该

    - (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
    
  3. 有一个例外情况是回调函数是一个通知引发的

    - (void)windowDidChangeScreen:(NSNotification *)notification;
    
  4. 'did'和'will'经常用在回调函数当做,标记'已经发生'或者'将要发生'

    - (void)browserDidScroll:(NSBrowser *)sender;
    - (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
    
  5. 'should'应用场景通常是询问代理行为是否应该发生,通常返回BOOL

    - (BOOL)windowShouldClose:(id)sender;
    

一些细节和例外情况

什么时候在变量名前加new/a

- (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate
- (void)setDragState:(MKAnnotationViewDragState)newDragState animated:(BOOL)animated NS_AVAILABLE(10_9, 4_2);

//NSString
- (void)setString:(NSString *)aString;
- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet;
  1. 我们看到当属性默认有初值的情况下,可以为get方法带的参数前添加一个new,比如newCoordinate,newDragState。
  2. 当参数名和内部变量名或者方法名可能冲突的, 或者变量名为string,set这一类类似类型名的参数,可在前面加数量词

以init开头时,我们通常是有返回值的,这是我们约定的一个习惯

小小的总结一下:

对于方法命名来说:

  1. 当制定一个操作动作时,以动词开头。一般定义他的返回值为void, 当需要得到他是否执行成功的状态时可以以BOOL作为返回值。
  2. 当需要返回值为对象数字等时,方法开头为名词,等同于返回值的类型或者含义。
  3. 在方法中有多个参数时,变量一般为名词(名词词组)或者形容词,参数前面的单词标记变量的含义和类型。
  4. 返回BOOL型时,在以形容词性单词或者词组作为方法名时,getter方法前加"is”

本文旨在探讨 objective-c中变量和方法的命名。 对于相关C语言系统方法,常量的定义,在苹果官方文档(链接在下面第一个)中就有很好的介绍。在此就先不讨论了。

注:参考资料:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html

https://google-styleguide.googlecode.com/svn/trunk/objcguide.xml

http://cocoadevcentral.com/articles/000082.php

你可能感兴趣的:(MKMapKit引发的对命名规范的讨论)