首页 > 代码库 > iOS开发代码规范

iOS开发代码规范

  作为一名合格的编码人员, 特别是打算以后长时间从事IT这一行业的人来说, 应该要明确一点: 我们写的代码不只是写给自己看的, 优雅的编码风格以及良好的代码注释对我们日常的开发是十分有必要的. 当我们在编写代码时, 要有一个意识: 此刻有千千万万的人正在看着自己写代码, 如何让他们都能快速看懂自己写的代码, 这就需要我们在平常开发的时候, 时时刻刻都要进行代码规范.

 

  • 命名规范
    • 常量
      • 常量应该使用相关的类的名字作为前缀, 并使用驼峰命名法
// 正例:
static NSStirng * const HomeViewControllerDidReceiveRefreshNotification = @”HomeViewControllerDidReceiveRefreshNotification”;
    
// 反例: 
static NSString * const refresh = @”refresh”;
    • 枚举
      • 枚举的命名应准确地描述该枚举的意义, 并使用驼峰命名法
      • 枚举中的各个值都应以定义的枚举类型开头, 其后跟随着枚举值对应的状态、选项或者类型.
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
    AFNetworkReachabilityStatusUnknown = -1,
    AFNetworkReachabilityStatusNotReachable = 0,
    AFNetworkReachabilityStatusReachableViaWWAN = 1,
    AFNetworkReachabilityStatusReachableViaWiFi = 2
};
    • 通知
      • 通知常用于模块间传递消息, 所以通知要尽可能地表示出发生的事件
      • [触发通知的类名] + [Did | Will] + [动作] + Notification
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
    • 代理
      • 类型实例必须为回调方法的参数之一
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
      • 回调方法的参数应该只包含类自己的情况, 方法名要符合实际含义
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
      • 以类的名字开头(回调方法存在两个以上参数的情况)以表明此方法是属于哪个类的
- (NSInteger)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
      • 使用 did 和 will 通知 Delegate 已经发生的变化或将要发生的变化
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (NSIndexPath *)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
    • 函数
      • 函数名称一般带有缩写前缀, 表示方法所在的框架
      • 函数前缀后的单词以驼峰表示法显示, 第一个单词首字母大写
      • 函数名的第一个单词是一个动词, 表示方法执行的操作
NSHighlightRect
NSDeallocateObject
      • 如果函数返回其参数的某个属性, 省略动词
unsigned int NSEventMaskFromType(NSEventType type)
float NSHeight(NSRect aRect)
      • 如果函数通过指针来返回值, 需要在函数名中便利 Get
const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
      • 函数的返回类型是 BOOL 时的命名
BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
    • 方法
      • 命名应该尽可能地清晰和简洁, 特点在 Object-C 中, 清晰比简洁更重要, 由于 Xcode 强大的自动补全功能, 我们不必担心名称过长的问题
//清晰
insertObject:atIndex:
//不清晰,insert的对象类型和at的位置属性没有说明
insert:at:
//清晰
removeObjectAtIndex:
//不清晰,remove的对象类型没有说明,参数的作用没有说明
remove:
      • 不要使用单词的简写, 拼写出完整的单词
//清晰
destinationSelection
setBackgroundColor:
 
//不清晰,不要使用简写
destSel
setBkgdColor:
      • 如果方法表示让对象执行一个动作, 使用动词打头来命名, 注意不要使用 do, dose 这种多余的关键字, 动词本身的暗示就足够了
//动词打头的方法表示让对象执行一个动作
- (void)invokeWithTarget:(id)target;
- (void)selectTabViewItem:(NSTabViewItem *)tabViewItem;
      • 如果方法是为了获取对象的一个属性值, 直接用属性名称来命名这个方法, 注意不要添加 get 或者或者其它动词前缀
//正确,使用属性名来命名方法
- (NSSize)cellSize;
 
//错误,添加了多余的动词前缀
- (NSSize)calcCellSize;
- (NSSize)getCellSize;
      • 可以使用 can, should, will, did 等词来协助表达存取方法的意思, 但不要使用 do, dose
//正确
- (void)setCanHide:(BOOL)flag;
- (BOOL)canHide;
- (void)setShouldCloseDocument:(BOOL)flag;
- (BOOL)shouldCloseDocument;
 
//错误,不要使用"do"或者"does"
- (void)setDoesAcceptGlyphInfo:(BOOL)flag;
- (BOOL)doesAcceptGlyphInfo;

 

 

  • 代码注释
    • 使用 #pragma mark - 方式对方法进行分组
 #pragma mark - private methods

- (void)samplePrivateMethod
{...}

- (void)sampleForIf
{...}
- (void)sampleForWhile {...}    - (void)sampleForSwitch {...} - (void)wrongExamples {...} #pragma mark - public methods - (void)samplePublicMethodWithParam:(NSString*)sampleParam {...} #pragma mark - life cycle methods - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {...} - (void)viewDidLoad {...}
    • 方法声明注释
#pragma mark - ?? 注册接口 ?? ??

/**
 *  注册接口
 *
 *  @param mobilePhone 注册手机号号
 *  @param areaId      手机区域号ID
 *  @param code        验证码
 *  @param pwd         注册密码
 */
- (NSURLSessionDataTask *)registerWithMobilePhone:(NSString *)mobilePhone
                                         areaId:(NSString *)areaId
                                             code:(NSString *)code
                                             pwd:(NSString *)pwd
                                           success:(SuccessBlock)success
                                           failure:(FailureBlock)failure;

 

 

  • 编码风格
    • 不要使用 new 方法
      • 尽管很多时候用 new 代替 alloc init 方法, 但是这可能导致高度内存时出现不可预料的问题, 并且 Cocoa 的规范就是使用 alloc init 方法, 使用 new 会让一些读者困惑  
    • Public API 要尽量简洁
      • 共有的接口要设计得简洁, 满足核心的功能需求就可以了, 不要设计很少会被用到并且参数极其复杂的 API , 如果定义复杂的方法, 使用类别或者拓展
    • initdealloc 方法中尽量不要用存取方法访问实例变量
      • initdealloc 方法被执行时, 类的运行时环境不是正常状态的, 使用存取方法访问变量可能会导致不可预料的结果, 因此应当在两个方法内直接访问实例变量
//正确,直接访问实例变量
- (instancetype)init {
  self = [super init];
  if (self) {
    _bar = [[NSMutableString alloc] init];
  }
  return self;
}
- (void)dealloc {
  [_bar release];
  [super dealloc];
}
 
//错误,不要通过存取方法访问
- (instancetype)init {
  self = [super init];
  if (self) {
    self.bar = [NSMutableString string];
  }
  return self;
}
- (void)dealloc {
  self.bar = nil;
  [super dealloc];
}
    • 保证 NSString 在赋值时被复制
- (void)setFoo:(NSString *)aFoo {
  _foo = [aFoo copy];
}
    • 当使用一个 CGRect 函数的 x, y, width, height 时, 应该使用 [CGGeometry 函数][CGGeometry-Functions_1]代替直接访问结构体成员
// 正例:
CGRect frame = self.view.frame;

CGFloat x      = CGRectGetMinX(frame);
CGFloat y      = CGRectGetMinY(frame);
CGFloat width  = CGRectGetWidth(frame);
CGFLoat height = CGRectGetHeight(frame); 

// 反例:
CGRect frame = self.view.frame;
    
CGFloat x     = frame.origin.x;
CGFloat y     = frame.origin.y;
CGFloat width  = frame.size.width;
CGFLoat height = frame.size.height;      
    • 嵌套判断
// 正例:
if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;

return YES;

 // 反例:
 BOOL isValid = NO;
if (user.UserName)
{
    if (user.Password)
    {
        if (user.Email) isValid = YES;
    }
}
return isValid;

 

iOS开发代码规范