美文网首页
iOS编码规范

iOS编码规范

作者: JakeyChen | 来源:发表于2018-08-01 17:15 被阅读0次

总体命名规则

  • 命名原则首先是要顾名思义。命名不要太随意,描述性的命名是最好的。
  • 变量名、方法名遵守驼峰命名法,首字母小写。
  • 类名、协议名、枚举类型遵守驼峰命名法,首字母大写,这些命名前面均需加前缀,推荐“JM“ + (功能简称)。
  • 常量名以k开头,如kJMTest。
  • 宏所有字母均大写,命名前面加前缀,推荐”JM“ + (功能简称)。
  • 通知Notification的格式 推荐 类/头文件名 + 进行状态(Will | Did) + 通知名称 + Notification
  • 协议名使用Delegate做后缀,DataSource使用DataSource做后缀。

头文件

  • 声明一个孤立的class或protocol 将声明放入单独的文件,
    使头文件名与声明的class/protocol相同。

  • 声明关联的class或protocol 将关联的声明(class/category/protocol)放入同一个头文件,头文件名与主要的class/category/protocol相同。

变量

变量尽量以描述性的方式来命名。单个字符的变量命名应该尽量避免,除了在for()循环。
星号表示变量是指针。例如, <font color=#FF69B4>NSString *text </font>既不是 <font color=#FF69B4>NSString* text</font> 也不是 <font color=#FF69B4>NSString * text</font>,除了一些特殊情况下常量。

  1. 类成员变量

    类中所有成员变量以属性的方式提供,不要使用其他类型的变量声明。

    通过使用'back'属性(_variable,变量名前面有下划线)直接访问实例变量应该尽量避免,除了在初始化方法 <font color=#FF69B4>(init, initWithCoder:</font>, 等…), <font color=#FF69B4>dealloc</font> 方法和自定义的<font color=#FF69B4>setters</font>和<font color=#FF69B4>getters</font>。如果要使用成员变量,必须以下划线'_'开头,如 NSString *_name。局部变量不应该包含下划线

    应该:

    @interface Test : NSObject
    @property (nonatomic, strong) NSString *name;
    @end
    

    不应该:

    @interface Test : NSObject {
     NSString *_name;
        }
    
  2. 私有属性应该声明在类的.m中,只有需要外部使用的变量才声明在.h中。

  3. 所有属性特性应该显式地列出来,有助于新手阅读代码。属性特性的顺序应该是atomicity、storage。

    应该:

    @property (nonatomic, strong) NSString *name;
    

    不应该:

    @property (nonatomic) NSString *testName;
    
  4. 属性是指针类型的集合时,格式如下:
    @property (nonatomic, strong) NSArray <Item *>*array;

  5. 属性是delegate时,声明成weak,防止循环引用,格式如下:
    @property(nonatomic, weak) id<UIScrollViewDelegate> delegate;

常量

常量应该使用static来声明而不是使用#define,除非显式地使用宏。

应该:

static NSString * const kJMAboutViewController = @"JMAboutViewController";
static CGFloat const kRowHeight = 50.0;

不应该:

#define kJMAboutViewController @"JMAboutViewController"
#define kRowHeight 2

布尔值

Objective-C使用的是BOOL值,对应的是YES和NO。true和false是bool类型,Objective-C不要使用。

既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较。

应该:

if (someObject) {
}
if (![anotherObject boolValue]) {
}

不应该:

if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.

如果BOOL属性的名字是一个形容词,属性就能忽略"is"前缀,但要指定get访问器的惯用名称。例如:
@property (assign, nonatomic, getter=isEditable) BOOL editable;

枚举型

普通枚举型

typedef NS_ENUM(NSInteger,JMTypeTest){
 kJMTypeA = 0, // 注释
 kJMTypeB = 1, // 注释
 kJMTypeC = 2, // 注释
 kJMTypeD = 3  // 注释
};

可按位或

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

字面值

NSString, NSDictionary, NSArray, 和 NSNumber的字面值应该在创建这些类的不可变实例时被使用。请特别注意nil值不能传入NSArray和NSDictionary字面值,因为这样会导致crash。

应该:

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @10018;

不应该:

NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingStreetNumber = [NSNumber numberWithInteger:10018];

操作符

一元操作符不带空格,多元运算符要带空格隔开。

  1. 逗号后面都要跟随一个空格。

    NSArray *array = @[1, 2, 3, 4];

  2. 二元操作符,如+, -, *, /,>, <, =, ==, ->, <<, >>等。

    a + b = c;
    a > b;
    a == b;
    a << 2;
    Object -> c;
    
  3. 三元操作符 Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

    应该:

    NSInteger value = 5;
    result = (value != 0) ? x : y;
    
    BOOL isHorizontal = YES;
    result = isHorizontal ? x : y;
    

    不应该:

    result = a > b ? x = c > d ? c : d : y;
    result = a ?: b;
    

集合类型

  • 如果初始值太长,元素需要换行,使⽤四个空格来进行缩进,右括号 ] 或者 } 写在新的⼀行,并且与调⽤语法糖那⾏代码的第一个非空字符对齐。
  • 构造字典时,字典的 Key 和 Value与中间的冒号都要留一个空格。
  • 需要使用类似”增删改查”方法来对集合进行操作时,方法名前面加上“add”、“remove”、“update”类似表明函数功能的关键字。
NSArray *array = @[ 
    @"This",
    @"is",
    @"an", 
    @"array"
];

NSDictionary *dictionary = @{
    NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12], 
    NSForegroundColorAttributeName : fontColor 
};

系统保留字

  • 系统保留字如if/else、do/while、try/catch 跟()和{}之间都要留一个空格。
  • 将关键字与花括号放在一行。
  • switch语句每个分支都必须用大括号包起来。
  • switch使用枚举型时,如果穷举完所有情况,不能有default分支。其他情况,都必须有default分支。

应该

if (a == 1) {
    NSLog(@"test1");
} else if (b == 2) {
    NSLog(@"test2");
} else {
    NSLog(@"test3");
}
switch (condition) {
  case 1:
    // ...
    break;
  case 2: {
    // ...
    // Multi-line example using braces
  }
    break;
  case 3:
    // ...
    break;
  default: 
    // ...
    break;
}

不应该

if(a==1){
    NSLog(@"test1");
}else if(b==2){
    NSLog(@"test2");
}else{
    NSLog(@"test3");
}

if条件语句

  • 条件语句主体为了防止出错应该使用大括号包围,即使只有一行代码。这些错误包括添加第二行代码和期望它成为if语句,但是没包含进大括号;还有,更危险的可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。

    应该:

    if (!error) {
        return success;
    }
    

    不应该:

    if (!error)
       return success;
    

    if (!error) return success;

  • 不要使用过多的分支,善于使用return来提前返回错误情况,把最正确的情况放到最后返回。

    应该

    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;
    
  • 条件过多,过长的时候应该换行。条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值,或者抽取出一个方法。

    应该

    if (condition1 && 
         condition2 && 
         condition3 && 
         condition4) {
        // Do something
    }
    
    BOOL finalCondition = condition1 && condition2 && condition3 && condition4
    if (finalCondition) {
       // Do something
    }
    
    if ([self canDelete]) {
       // Do something
    }
    
    - (BOOL)canDelete{
        BOOL finalCondition1 = condition1 && condition2;
        BOOL finalCondition2 =  condition3 && condition4;
        return condition1 && condition2;
    }
    

    不应该

    if (condition1 && condition2 && condition3 && condition4) {
        // Do something
    }
    
  • if条件语句是和常量比较时,多于3个,使用switch/case实现。

函数声明

  • 方法名和参数尽量读起来像是一句话。
  • 方法名不允许使用 get、 do 或does 做前缀,动词本身的暗示就够了。
  • 如果方法是为了获取对象的一个属性值,直接用属性名称来命名方法,不要添加 get 或其他的动词。
  • and 和 with 不应该用于多个参数来说明。
  • 方法类型(-/+)和返回值之间要有一个空格。
  • 参数类型和指针符之间要留空格。
  • 参数类型和冒号之间不需要留空格。
  • 参数类型和参数名之间不需要留空格。

应该

- (UIView *)initWithTitle:(NSString *)title backgroundColor:(UIColor *)color;

不应该

-(UIView *)initWithTitle:(NSString *)title withBackgroundColor: (UIColor*) color;

函数实现

  • { 和函数名可以在同一行,也可以换行。但是一个类文件里面要保持统一风格。修改者要遵循创建者的风格。
  • 函数和函数之间空一行。
  • 内部实现时,花括号的嵌套要注意对齐。也可以全部选中,然后使用快捷键control+I。
  • 每行建议不超过120个字符,函数名过长时,使用冒号对齐的方式。
  • 如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数。
  • 将函数内部比较复杂的逻辑提取出来作为单独的函数。
  • 尽量避免空方法的产生。
- (void)popBack {
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
                                       URLString:(NSString *)URLString
                                      parameters:(id)parameters
                                  uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
                                downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
                                         success:(void (^)(NSURLSessionDataTask *, id))success
                                         failure:(void (^)(NSURLSessionDataTask *, NSError *))failure

.m文件整理

  • 如果有实例化函数应该放在最前面。
  • 如果是view或者viewController的.m文件,应该按照loadView、viewDidLoad、viewWillApperar、viewDidAppear、viewWillDisappear、viewDidDisapper和dealloc的顺序实现,并且这些生命周期相关的函数放在最前面。
  • 后面再跟viewWillLayoutSubviews和viewDidLayoutSubviews等这些布局相关的函数。
  • setter、getter同类型方法放在一起,并用pragma mark - setter方法 或 pragma mark - getter方法进行标注。
  • 相同delegate和datasource的函数放在一起,并用pragma mark - xxx标注。

init方法

Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id。

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

类构造方法

当类构造方法被使用时,它应该返回类型是instancetype而不是id。这样确保编译器正确地推断结果类型。

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

回调方法

函数调用的可知性,回调时被调用者要知道其调用者,方便信息的传递,所以建议在回调方法第一个参数中加上调用者。除非只有一个名为sender的参数。

如:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)windowShouldClose:(id)sender;

CGRect函数

当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。引用Apple的CGGeometry:

应该:

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

不应该:

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;
CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };

单例模式

单例对象应该使用线程安全模式来创建共享实例。

+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

Block

  • 较短的Block可以写在一行内。
  • 如果block过于庞大,应该使用typedef单独声明成一个变量来使用。
    typedef void (^AFURLSessionDidBecomeInvalidBlock)(NSURLSession *session, NSError *error);
  • 如果分行显示的话,block的右括号应该和调用block那行代码的第一个非空字符对齐。
  • block内的代码注意缩进对齐。
  • ^ 和左括号 ( 或者 { 之间没有空格,参数列表的右括号 ) 和 {之间有一个空格。
__weak typeof(manager)wManager = manager;
    
[manager POST:url parameters:reqDic progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [wManager invalidateSessionCancelingTasks:false];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [wManager invalidateSessionCancelingTasks:false];
    }];
    

图片资源命名

图片资源命名方式,以 模块 + 功能 为组织形式,如果相同功能有多个,比如表情,则再加编号。

如:

tabbar_item_1.png
tabbar_item_2.png

maitoutiao_topicon_1.png
maitoutiao_topicon_2.png

注释

  • 使用 // 注释时,后面要加一个空格,如果注释+被注释文本过长,使用/*xxx*/。
  • 对函数注释时,将光标移到函数行,使用option+command+/快捷键来生成注释。
  • 对delegate和dataSource实现时,在第一个函数前面#pragma mark - xxx来进行标注。

警告⚠️

尽量减少⚠️的产生,能修改的要及时修改。

相关文章

  • iOS开发 | 规范编码的四个意识

    iOS开发 | 规范编码的四个意识 iOS开发 | 规范编码的四个意识

  • iOS 编码规范

    Table of Contents iOS 编码规范1 文件规范1.1 文件编码1.2 文件命名2 编码格式2.1...

  • iOS(Objective-C)编码规范

    iOS(Objective-C)编码规范 本文件旨在统一****iOS方向编码规范。增强代码可读性,便于后期维护。...

  • 20170317 Guidelines & AppSto

    Guidelines iOS开发规范整理 Objective-C编码规范:26个方面解决iOS开发问题 iOS开发...

  • iOS 代码规范文档

    iOS 代码规范文档 [toc] 修订 概述 制定目的:制定iOS 编码规范,主要是为了规范公司内部的iOS 代码...

  • 雷铭大前端组件库

    雷铭大前端组件库 包含《雷铭前端开发规范》、《雷铭Android编码规范》、《雷铭iOS编码规范》以及不同技术分类...

  • iOS编码规范

    iOS编码规范 GitHub 地址https://github.com/CodeOuyang/iOS-note.g...

  • 2018-08-13

    浅谈iOS编码规范 命名 awakeFromNib不能拿到真实尺寸

  • iOS编码规范

    目录 核心原则 命名 文件命名 视图命名 方法命名 变量命名 图片命名 代码格式 空格 函数的书写 函数调用 协议...

  • iOS 编码规范

    约定 在我看来,开发规范像是一条可供参考的标准线。不同开发者可以根据这条标准线来规范自己的开发行为,尤其是在大的项...

网友评论

      本文标题:iOS编码规范

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