博客> IOS 编码规范整理
IOS 编码规范整理
2017-11-25 08:54 评论:0 阅读:521 流浪猫_qihr
ios 基础 编码规范 风格

本文参考自 《苹果Cocoa编码规范》 《objective-c-style-guide》 ,希望能够启动抛砖引玉的功能。而且会持续更新,逐步细化。

命名规则

一般原则

清晰

  • 命名最好清晰又简洁,但是不能过于简洁而失去了清晰。
    insertObject:atIndex: //这是个好例子
    insert:at: //不够清晰,插入的是什么?at又指的是什么?
  • 名称通常不缩写,即使名称很长,也要拼写完全
代码 点评
destinationSelection Good.
destSel Not clear.
setBackgroundColor: Good.
setBkgdColor: Not clear.

关于缩写,你可能会认为某个缩写广为人知,但有可能并非如此,尤其是当你的代码被来自不同文化和语言背景的开发人员所使用时。当然,你可以使用少数非常常见,历史悠久的缩写。所有可以使用的缩写见这个列表

建议:类、变量和函数命名参考苹果的头文件及优秀的开源项目。

一致性

  • 尽可能使用与 Cocoa 编程接口命名保持一致的名称。
  • 在使用多态方法的类中,命名的一致性非常重要。在不同类中实现相同功能的方法应该具有相同的名称

前缀

由于苹果没有命名空间,因此使用前缀可以防止和苹果以及其他第三方库的命名冲突。

  • 不要使用下划线或子前缀
  • 对class、protocol、structure、公开方法、常量命名时使用前缀,成员方法和结构体字段时不使用前缀。
  • Category 方法统一使用xy_methodName 形式进行扩展

书写约定

  • 命名时单词之间不要使用下划线、破折号等标点符号分隔,请使用驼峰命名法对方法、变量进行命名
  • 如果方法名使用一个广为人知的大写首字母缩略词开头,则首字母可以大些。如NSImage  中的TIFFRepresentation

  • 如果方法名或者常量名使用了前缀,则前缀之后所有单词的首字母都要大写。如NSRunAlertPanel

避免使用下划线来表示名称的私有属性。苹果公司保留该方式的使用。如果第三方这样使用可能会导致命名冲突,他们可能会在无意中用自己的方法覆盖掉已有的私有方法,这会导致严重的后果

类和协议的命名

类名应该包含明确描述该类/对象是什么或者做什么的名词,类名要有合适的前缀。 协议应根据它包含的方法的作用来命名。

  • 大多数协议仅组合一组相关的方法,而不关联任何类,这种协议的命名应该使用动名词(ing),以不与类名混淆 比如NSLocking
  • 有些协议组合一些彼此无关的方法(这样做是避免创建多个独立的小协议)。这样的协议倾向于与某个类关联在一起,该类是协议的主要体现者。在这种情形,我们约定协议的名称与该类同名。NSObject 协议就是这样一个例子。这个协议组合一组彼此无关的方法,有用于查询对象在其类层次中位置的方法,有使之能调用特殊方法的方法以及用于增减引用计数的方法。由于 NSObject 是这些方法的主要体现者,所以我们用类的名称命名这个协议。

头文件

  • 声明孤立的类或协议:将孤立的类或协议声明放置在单独的头文件中,该头文件名称与类或协议同名
  • 声明相关联的类或协议:将相关联的声明(类,类别及协议) 放置在一个头文件中,该头文件名称与主要的类/类别/协议的名字相同
    
    @protocol SDWebImageManagerDelegate <NSObject>

@optional

  • (BOOL)imageManager:(nonnull SDWebImageManager )imageManager shouldDownloadImageForURL:(nullable NSURL )imageURL; @end

@interface SDWebImageManager : NSObject

@property (weak, nonatomic, nullable) id delegate; // ... @end

## 方法命名规范
### 一般规则
- 小写第一个单词的首字符,大写随后单词的首字符,不使用前缀。有两种例外情况:
1,方法名以广为人知的大写字母缩略词(如TIFF or PDF)开头;
2,私有方法可以使用统一的前缀来分组和辨识

- 表示对象行为的方法,名称以动词开头

```Objc
- (void)invokeWithTarget:(id)target:
- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem

名称中不要出现 do 或 does,因为这些助动词没什么实际意义。也不要在动词前使用副词或形容词修饰

  • 方法返回接收者的某个属性,直接用属性名称命名。
- (NSSize)cellSize; //优
- (NSSize)calcCellSize; //差
- (NSSize)getCellSize; //差

只有在方法需要间接返回多个值的情况下,才使用 get

//NSBezierPath
- (void) getLineDash:(float *)pattern count:(int *)count phase:(float *)phase; 

像上面这样的方法,在其实现里应允许接受 NULL 作为其 in/out 参数,以表示调用者对一个或多个返回值不感兴趣。

  • 参数前面的单词要能描述该参数
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag; //优
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; //差
- (id)viewWithTag:(int)aTag; //优
- (id)taggedView:(int)aTag; //差
  • 细化基类中的已有方法:创建一个新方法,其名称是在被细化方法名称后面追加参数关键词
//NSView
- (id)initWithFrame:(NSRect)frameRect;
//NSMatrix - NSView 的子类
- (id)initWithFrame:(NSRect)frameRect 
               mode:(ind)aMode 
          cellClass:(Class)factoryId 
       numberOfRows:(int)rowsHigh
    numberOfColumns:(int)colsWide;
  • 不要使用 and 来连接用属性作参数关键字
- (int) runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; //优
- (int) runModalForDirectory:(NSString *)path addFile:(NSString *)name addTypes:(NSArray *)fileTypes; //差

访问方法

  • 如果属性是用名词描述的,则命名格式为:
- (type)noun;
- (void)setNoun:(type)aNoun;

如:

- (NSColor *)color;
- (void)setgColor:(NSColor *)aColor;
  • 如果属性是用形容词描述的,则命名格式为:
- (BOOL)isAdjective;
- (void)setAdjective:(BOOL)flag;

如:

- (BOOL)isEditable;
- (void)setEditable:(BOOL)flag;
  • 如果属性是用动词描述的,则命名格式为:(动词要用现在时时态)
- (BOOL)verbObject;
- (void)setVerbObject:(BOOL)flag;

例如:

- (BOOL)showsAlpha;
- (void)setShowAlpha:(BOOL)flag;
  • 不要使用动词的过去分词形式作形容词使用
- (BOOL)acceptsGlyphInfo; //优
- (void)setAcceptsGlyphInfo:(BOOL)flag; //优
- (BOOL)glyphInfoAccepted; //差
- (void)setGlyphInfoAccepted:(BOOL)flag; //差
  • 可以使用情态动词(can, should, will 等)来提高清晰性,但不要使用 do 或 does
- (BOOL)canHide; //优
- (void)setCanHide:(BOOL)flag; //优
- (void)shouldCloseDocument; //优
- (void)setShouldCloseDocument:(BOOL)flag; //优
- (BOOL)doseAcceptGlyphInfo; //差
- (void)setDoseAcceptGlyphInfo: //差

Delegate Methods 委托方法

委托方法是那些在特定事件发生时可被对象调用,并声明在对象的委托类中的方法。它们有独特的命名约定,这些命名约定同样也适用于对象的数据源方法。

  • 名称以标示发送消息的对象的类名开头,省略类名的前缀并小写类第一个字符
- (BOOL)tableView:(UITableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
  • 冒号紧跟在类名之后(随后的那个参数表示委派的对象)。该规则不适用于只有一个 sender 参数的方法
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
  • 上面的那条规则也不适用于响应通知的方法。在这种情况下,方法的唯一参数表示通知对象
- (void)windowDidChangeScreen:(NSNotification *)notification;
  • 用于通知委托对象操作即将发生或已经发生的方法名中要使用 did 或 will
- (void)browserDidScroll:(NSBrowser *)sender;
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window;
  • 用于询问委托对象可否执行某操作的方法名中可使用 did 或 will,但最好使用 should
- (BOOL)windowShouldClose:(id)sender;

Collection Methods 集合方法

  • 管理对象(集合中的对象被称之为元素)的集合类,约定要具备如下形式的方法:
- (void)addElement:(elementType)adObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;

例如:

- (void)addLayoutManager:(NSLayoutManager *)adObj;
- (void)removeLayoutManager:(NSLayoutManager *)anObj;
- (NSArray *)layoutManagers;

集合方法命名有如下一些限制和约定:

  • 集合中的元素无序,返回 NSSet,而不是 NSArray
  • 将元素插入指定位置的功能很重要,则需具备如下方法:
- (void)insertElement:(elementType)anObj atIndex:(int)index;
- (void)removeElementAtIndex:(int)index;

集合方法的实现要考虑如下细节:

  • 以上集合类方法通常负责管理元素的所有者关系,在 add 或 insert 的实现代码里会 retain 元素,在 remove 的实现代码中会 release 元素
  • 当被插入的对象需要持有指向集合对象的指针时,通常使用 set... 来命名其设置该指针的方法,且不要 retain 集合对象。比如上面的 insertLayerManager:atIndex: 这种情形,NSLayoutManager 类使用如下方法:
- (NSTextStorage *)textStorage;
- (void) setTextStorage:(NSTextStorage *)textStorage;

通常你不会直接调用 setTextStorage:,而是覆写它。另一个关于集合约定的例子来自 NSWindow 类:

- (void)addChildWindow:(NSWindow *)childWin ordered:(NSWindowOrderingMode)place;
- (void)removeChildWindow:(NSWindow *)childWin;
- (NSArray *)childWindows;
- (NSWindow *)parentWindow;
- (void)setParentWindow:(NSWindow *)window;

Method Arguments 方法参数

命名方法参数时要考虑如下规则:

  • 如同方法名,参数名小写第一个单词的首字符,大写后继单词的首字符。如:removeObject:(id)anObject
  • 不要在参数名中使用 pointer 或 ptr,让参数的类型来说明它是指针
  • 避免使用 one, two,...,作为参数名
  • 避免为节省几个字符而缩写  按照 Cocoa 惯例,以下关键字与参数联合使用:
    ...action:(SEL)aSelector
    ...alignment:(int)mode
    ...atIndex:(int)index
    ...content:(NSRect)aRect
    ...doubleValue:(double)aDouble
    ...floatValue:(float)aFloat
    ...font:(NSFont *)fontObj
    ...frame:(NSRect)frameRect
    ...intValue:(int)anInt
    ...keyEquivalent:(NSString *)charCode
    ...length:(int)numBytes
    ...point:(NSPoint)aPoint
    ...stringValue:(NSString *)aString
    ...tag:(int)anInt
    ...target:(id)anObject
    ...title:(NSString *)aString

rivate Methods 私有方法

大多数情况下,私有方法命名相同与公共方法命名约定相同,但通常我们约定给私有方法添加前缀,以便与公共方法区分开来。即使这样,私有方法的名称很容易导致特别的问题。当你设计一个继承自 Cocoa framework 某个类的子类时,你无法知道你的私有方法是否不小心覆盖了框架中基类的同名方法。 Cocoa framework 的私有方法名称通常以下划线作为前缀(如:_fooData),以标示其私有属性。基于这样的事实,遵循以下两条建议:

  • 不要使用下划线作为你自己的私有方法名称的前缀,Apple 保留这种用法。
  • 若要继承 Cocoa framework 中一个超大的类(如:NSView),并且想要使你的私有方法名称与基类中的区别开来,你可以为你的私有方法名称添加你自己的前缀。这个前缀应该具有唯一性,如基于你公司的名称,或工程的名称,并以“XX_”形式给出。比如你的工程名为"Byte Flogger",那么就可以是“BF_addObject:” 尽管为私有方法名称添加前缀的建议与前面类中方法命名的约定冲突,这里的意图有所不同:为了防止不小心地覆盖基类中的私有方法。

变量

一般变量

变量使用驼峰命名法。命名时应使用完整的能表示变量作用的词语,禁止使用缩略词或者意义表示不明的词语。错误例子

int w;
int nerr;
int nCompConns;
tix = [[NSMutableArray alloc] init];
obj = [someObject object];
p = [network port];

正确例子

int numErrors;
int numCompletedConnections;
tickets = [[NSMutableArray alloc] init];
userInfo = [someObject object];
port = [network port];

常量

常量名称前统一加字母“k”,也可以是你认为可以表示为常量的字母,比如“c”

const int kNumberOfFiles = 12;
NSString *const kUserKey = @"kUserKey";
enum DisplayTinge { 
    kDisplayTingeGreen = 1, 
    kDisplayTingeBlue = 2
};

空格与格式

  • 方法的声明和定义在-号或者+号与返回值之间应留一个空格。而返回值与方法名以及方法名和参数列表之间都不应该有空格例如
- (void)doSomethingWithString:(NSString *)theString { ...}

推荐使用容器符号([]以及{})来表示数组和字典。容器符号与内容之间应使用空格分开

NSArray* array = @[ [foo description], @"Another String", [bar description] ];
NSDictionary* dict = @{ NSForegroundColorAttributeName : [NSColor redColor] };

注释

  • 声明注释 每个类、category和protocol都需在注释中说明它的用途以及在整个框架中的作用

  • 变量注释 尽可能详细地对关键变量进行注释,说明其作用
  • 语句注释 在核心代码或者关键逻辑处必须进行注释,注释时不光要说明这段代码的作用,更要描述清楚为什么要这么做

排版

  • 关键词和操作符之间加适当的空格。 [必须] 正确的示范:
    int count = 5; //注意 = 之间的空格
    错误的示范:
    int count=5;
  • 相对独立的程序块与块之间加空行。 [建议]

    
    正确的示范:
    if(retryCount > 3) {
    return;
    }
                              //注意这里空一行
    for(int i = 0; i < 100 nCount=5;> 3) {
    return;
    }
    for(int i= 0; i < 100>> 16)  | \
    (((unsigned int)(x)) & 0x0000ff00 )  | \
    (( ((unsigned int)(x)) & 0x000000ff) << 16 xss=removed xss=removed i=0;int j=0; xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed getter=isEditable) xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed xss=removed>但是目前该选项对Swift不起作用,未来苹果完善Swift之后,出现warning会导致编译通不过。
    开发过程中注意Xcode控制台输出的警告信息,消除这些警告。目前来看AutoLayout相关的警告会比较多。
  • 基于UITableView写页面,基于BaseUITableViewController和RefreshTableViewController作为VC的基类,保证交互的一致性。UITableview构建UI好处如下:

    • 1.UITableview是数据驱动的,根据内置的机制做展现。修改dataSource就可以控制界面的展现。刷新界面时只需要使用reloadData和reloadRowsAtIndexPaths就能完成。
    • 2.页面扩展能力佳。未来业务发展,需要在页面最上面最下面增加内容,对之前代码做少量修改就能做到。
    • 3.大量实现下拉刷新功能的开源项目,如果页面需要刷新功能,轻松做到。
    • 4.UITableview具有强大的表现能力,再复杂的页面都可以完成。
    • 5.UITableview使得交互更加统一,所有的页面,不管内容多少,都可以scroll和bounce,符合iOS的交互习惯。可以参看iOS自带的App的交互。
    • 6.UITableView隐藏着很多高级特性(继承自UIScrollView),比如automaticallyAdjustsScrollViewInsets让内容可以滚动到导航栏和tabbar的后面,配合磨砂效果,视觉效果非常赞;比如点击状态栏会滚动到顶部;这些特性不需要开发就与生俱来。
    • 7.iOS 8新增Self Sizing Cell,只需要设置rowHeight为UITableViewAutomaticDimension就可以实现cell高度根据约束自动调整(务必设置top和bottom与contentView的约束),cell高度计算和缓存已经成为历史往事。
    • 8.滚动到页面指定的位置非常方便。并且通过保存UITableview的contentOffset,可以记住页面滚动的位置,当用户进入相同的页面,滚动到之前的位置。
    • 9.对于拥有许多输入项的页面,将UITableView的keyboardDismissMode设置成interactive,轻松拥有MessageApp那样灵动的交互。
  • 多用block替代delegate,比如UITableViewCell的点击操作,block比delegate代码更为简洁直观。
  • 除了CRUD里面的CUD、下拉刷新和上拉翻页等,绝大部分接口都应该使用缓存,提高用户体验。
  • 尽量使用枚举类型要标识状态,提高代码可读性和可维护性。Swift支持字符串作为枚举类型的值,使用根据方便。
  • 使用registerClass:forCellReuseIdentifier:+dequeueReusableCellWithIdentifier:forIndexPath: 接口做cell复用。cell可以很多时,一定要坚持做cell复用,提高效率。
  • 版本系统API时,通用功能不要引入高版本API。 。

总结

编码规范,没有固定的版本,也没有明确的对错。但是对于完善的开发团队,明确的规范,可以为后期的代码维护,新人学习带来很多好处。还有就是规范,可以将开发维护过程中,前人犯过的错误,进行总结,写入内部编码规范内,起到避免重复犯错的作用。

收藏
0
sina weixin mail 回到顶部