iOS开发-Objective-C语言代码规范-OC代码规范集合-iOS代码规范

2023-05-16

为什么要做代码规范?

目前的项目大多都是由一个团队来完成,如果没有统一的代码规范,那么每个人的代码必定会风格迥异,在工作中肯定会有多个人同时开发同一模块的情况,即使是分工十分明晰的,等到整合代码、CodeReView、工作接力等情况时问题就会显现出来。统一的风格使得代码可读性大大提高了,人们看到任何一段代码都不用去浪费更多的时间去琢磨。规范不是对开发的制约,而确实是有助于提高开发效率的。,规范的代码在团队的合作开发中是非常有益而且必要的。

代码规范能带来什么好处?

1.规范的代码可以促进团队合作

在文章开篇的时候就已经提及了:统一的风格使得代码可读性提高,在同事之间看到任何一段代码都不用去浪费更多的时间去琢磨,可以高效的完成开发工作。

2.规范的代码可以减少bug处理

有规范的对参数进行输入输出,有规范的异常处理,没规范的日志处理等等,bug不但可以有效减少,查找bug也变得轻而易举。

3.规范的代码可以降低维护成本

开发过程中的代码质量直接影响着维护的成本,可读性高的代码维护成本必然会大大降低。 而且,维护工作不仅仅是读懂原有代码,还需要在原有代码基础上作出修改,因此,统一的风格有利于长期的维护。

4.规范的代码有助于代码审查

代码审查可以及时纠正一些错误,对开发人员的代码规范作出监督。团队的代码审查同时也是一个很好的学习机会,对成员的进步也是很有益的,同时代码审查也有助于代码规范的实施。

5.养成代码规范的习惯,有助于自身的成长

有很多时候去看自己曾经写得代码是不是没有头绪呢?尤其是出现bug的时候需要逐行的debug?我们应该做的就是规范开发,减少自己出现的错误,规范开发最大的受益人其实是自己。


下面开始我们的正题:代码规范

《iOS技术部门代码规范1.0范本》

本文整合了谷歌、raywenderlich.com、58到家、NetStars(日)公司在开发过程中关于iOS端(Objective-C语言)的代码规范,适用于中小型团队(0-30人协作)。

核心原则意义
原则一:代码应该简洁易懂,逻辑清晰软件是需要人来维护的,不要过分追求技巧,降低程序的可读性。
原则二:面向变化编程,而不是面向需求编程不能仅仅为了当前的需求,写出扩展性强,易修改的程序才是负责任的做法,对自己负责,对公司负责。
原则三:先保证程序的正确性,防止过度工程过度工程(over-engineering):在正确可用的代码写出之前就过度地考虑扩展,重用的问题,使得工程过度复杂。

引用改编自 https://www.jianshu.com/p/d7e87107073c

目录:

  • 注释的写法
  • 命名与规范
  • 分类的方法规范
  • 代码组织结构规范
  • 条件语句规范
  • 补充规范

注释的写法

首先我们就从大家最关心的“代码注释”作为切入,虽然写起来很痛苦,但注释是保证代码可读性的关键。下面的规则给出了你应该什么时候、在哪进行注释。注释很重要,好的代码应该能自成文档。与其给类型及变量起一个晦涩难懂的名字,再为它写注释,不如直接起一个有意义的名字。当你写注释的时候,记得你是在给你的听众写,即下一个需要阅读你所写代码的贡献者。大方一点,下一个读代码的人可能就是你?

1.Class 类注释

每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。如下:

/**
 委托代理,用来处理关于app的启动和关闭过程中的xxx、xxx事件,被app的xxx控制器拥有。
*/ 
@interface MyAppDelegate : NSObject {
  ...
}
@end
2.Property 属性注释
/**
 标识是起始地还是目的地
 */
@property (nonatomic, copy) NSString *userName;

/**
 地址信息模型
 */
@property (nonatomic, copy) UserInfoModel *userInfoModel;
3.Method 方法声明注释
/**
 登录倒计时文本
 @param timerLabel 倒计时Label对象
 @param timeText   倒计时文本数据
*/
- (void)timerLabel:(UILabel *)timerLabel timeText:(NSString *)timeText;
4.Block 代码块注释
/**
 验证搜索成功失败模块
 @param searchResponse 搜索返回结果
 @param error 错误信息
 @return 如果返回 YES 代表搜索成功, 返回 NO 代表搜索失败
*/
typedef BOOL(^SearchResultBlock)(AMapPOISearchResponse *searchResponse, NSError *error);
5.NSEnum 枚举注释
/**
(参考YYText)
YYTextVerticalAlignmentTop: 顶部对齐
YYTextVerticalAlignmentCenter: 居中对齐
YYTextVerticalAlignmentBottom: 底部对齐
*/
typedef NS_ENUM(NSInteger, YYTextVerticalAlignment) {
    YYTextVerticalAlignmentTop =    0, ///< Top alignment.
    YYTextVerticalAlignmentCenter = 1, ///< Center alignment.
    YYTextVerticalAlignmentBottom = 2, ///< Bottom alignment.
};
6.局部变量注释
@interface SomeViewController () {
    SomeModel * someModel; // 用来存储信息
    NSString * someStr;    // 用来检测的字段
    UIView * someView;     // 用来显示信息的视图
}
7.Method 方法实现注释①
/** 地址选择确认返回上一页 */
- (void)doSomeThing:(id)someObject{
    /**
     判断内容和结果
    */ 
    if (条件判断1) {
        if (内部条件) {
            do...
        }else{
            do...
        }
    }
    
    /**
     判断内容和结果
    */ 
    if (条件判断2) {
        do...
        return;
    }
    
    /**
     [回调数据][目标接受页面]
     tips:当block嵌套太多的时候,对于读代码的人来说非常困惑,因为往往可能追了3,4个block进去之后,被以下这点代码传出去了,所以,在使用block的时候,花点时间注明回调的数据以及目标页面方便自己也方便别人
    */ 
    _someBlock(xxx);
}
8.Method 方法实现注释②
// tips:对于一个不常用的第三方、底层类库等代码,可以简单的把每一个过程都写一下,这样的话易读性很强
- (void)createCAEmitterLayer{
    // 1.创建CAEmitterLayer
    CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
    
    // 2.设置CAEmitterLayer的属性(最主要的是前四个)
    // 发射源的形状 是枚举类型
    emitterLayer.emitterShape = kCAEmitterLayerLine;
    
    // 发射模式 枚举类型
    emitterLayer.emitterMode = kCAEmitterLayerSurface;
    
    // 发射源的size 决定了发射源的大小,如果做了倾斜或者便宜屏幕宽度是不够的,那时候就需要自定义
    emitterLayer.emitterSize = self.view.frame.size;
    
    // 发射源的位置
    emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10);
    
    // 渲染模式 枚举类型 (⭐️这个渲染模式表达效果会很不好,不常用,kCAEmitterLayerAdditive,可以让重叠的部分高亮)
    emitterLayer.renderMode = kCAEmitterLayerAdditive;
    
    // 3.添加到目标视图的layer上
    [self.view.layer addSublayer:emitterLayer];
}
9.Method 方法实现注释③
/** 
 登录请求验证
 @param userId 用户名
 @param password 密码
 @param complete 执行完毕的block
*/
- (void)loginRequestWithUserId:(NSString *)userId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete{
	
}

命名的规范

1.通用变量命名

保持使用驼峰命名法,建议的写法如下:

@property (nonatomic, copy) NSArray *childPaths;

反例:

@property (nonatomic, copy) NSArray *childpaths;
2.宏命名

不带参数情况下,全部大写,单词间用 _ 分隔,建议写法如下:

#define THIS_IS_A_TICKET @"THIS_IS_A_TICKET"

不带参数情况下,避免和类自己的参数冲突可以以【小写】字母k或者自定义字母作为开头,后面遵循驼峰命名,建议写法如下:

#define kScreenWidth [UIScreen mainScreen].bounds.size.width

带参数的情况下,遵循驼峰命名,建议写法如下:

#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]

反例:

#define homepageadbannerheight 85
3.控件命名

控件的命名需要注意突出后缀,建议写法如下:

UIView *userInfoView;

反例:

UIView *userInfo;

关键是让人通过命名就能知道其目的或者用法,一般控件的命名都是接上所生命类的后缀,示例情况如下:

UI控件名自定义名
UIViewsomeView
UViewControllersomeViewController
UIButtonsomeButton
UILabelsomeLabel
UITableViewsomeTableView
4.类的命名①

一般在自定义类的时候需要注意命名规范,首字母大写每个单词首字母大写(大驼峰命名法),建议写法如下:

@interface UserInfoManager : NSObject
5.类的命名②

一般在不同业务线上会有不同分支的首页、子页面,很多时候会有重复的信息,比如两个模块都有HomePage,这时候就需要在前面加上前缀,建议写法如下:

/**
 活动页面
*/
@interface ActivityHomeViewController : UIViewController

反例:

@interface HomeViewController : UIViewController

分类的方法规范

我们在开发过程中为了更好的使用系统方法,一般会使用分类来添加方法,来满足开发需要,分类的作用就是在不修改原有类的基础上,为一个类扩展方法,最主要的是可以给系统类扩展我们自己定义的方法。为了避免和系统方法或者别的同事所做的分类方法的冲突,一般都会在方法面前添加小写字母来做区别,建议写法如下:

@interface UIView (TouchBlock)
- (void)tb_touchView:(void (^)(void))block;
@end

反例:

@interface UIView (TouchBlock)
- (void)touchView:(void (^)(void))block;
@end

代码组织结构规范

1. #import 文件的顺序

在开发过程中,使用#import来导入头文件是必不可少的工序,但有的功能或者业务模块需要导入的文件就会非常多,看着会很别扭,所以指定一套方案来改善这一现状是很有必要的,建议顺序如下:

// 系统库
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

// 第三方库
#import <Masonry.h>

// 自定义类
#import "MyButton.h" 
#import "UserInfoModel.h"
2.控制器中方法分区

一般在控制器中,会有控制器生命周期相关方法以及不同的业务代码,可以使用#pragma mark来进行简单的方法分区,建议写法如图:
在这里插入图片描述
可以稍做优化,区分为几个模块,示例如下:

#pragma mark - Life Cycle 生命周期
#pragma mark - Request 网络请求
#pragma mark - Delegate 代理实现
#pragma mark - Event Response 事件响应
#pragma mark - Methods 方法

根据每个人的喜好不一致可以按照自己的想法来写,这样在别人梳理文件的时候思路会比较清晰


条件语句规范

1.{}括号的规范

一般会出现以下几种情况:

// 1
if (YES) {
    Do();
}

// 2
if (YES) DO();

// 3
if (YES)
   Do();
   
// 4
if (YES) 
{
   Do(); 
}

以上第一种方式是苹果主推的,经过调研,方法2、3、4虽然在写法上会有所简化,但在风格上看来还是有所欠缺,所以更多的建议使用第一种。

2.减少逻辑嵌套

一般在判断的时候可以使用最简单的方法来做最简单的事情,使用return可以减少复杂度,提高代码可读性,建议写法如下:

- (void)someMethod {
    if(![someThing]) {
        return;
    }
    // continue do something 
}

反例:

- (void)someMethod {
    if([someThing]) {
        // continue do something
    }else{
    	return;
    }
}
3.复杂表达式

常用写法:

if ([self getUserInfoModel] == nil && ![self userLogin]){
	[self showLoginVC];
}

一般会有很长的表达式或者多个待判断的值,在这种情况下,建议以下写法:

BOOL userInfoModelIsNil  = [self getUserInfoModel] == nil;
BOOL userIsLogin = ![self userLogin];
BOOL showLoginVC = userModelIsNil && userIsLogin;
if (showLoginVC) {
    [self showLoginVC];
}

第一种写法大家都是很常用的,表达很简洁,但是从阅读代码和调试代码的角度看,推荐第二种,因为每个条件和句子的意义很明显的就能看出来。


补充规范

1.如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐,建议写法如下:

- (void)doSomethingWithUserName:(NSString *)name
                        address:(NSString *)address
                     doorNumber:(float)number {
  ...
}

当第一个关键字比其它的短时,保证下一行至少有 4 个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:

- (void)getA:(AClass *)A
    longKeywordB:(BClass *)B
    evenLongerKeywordC:(CClass *)C {
  ...
}

2.常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。建议写法如下:

static NSString * const XXXAboutViewControllerCompanyName = @"XXXCompanyName";
static CGFloat const XXXImageThumbnailHeight = 50.0;

反例:

#define CompanyName @"XXXCompanyName"
#define ImagethumbnailHeight 50

3.布尔值的判断书写,Objective-C使用YES和NO。因为true和false应该只在CoreFoundation,C或C++代码使用。既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,因为YES被定义为1和一个BOOL能被设置为8位。这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。建议书写方式如下:

if (someObject) {

}
if (![anotherObject boolValue]) {

}

反例:

if (someObject == nil) {

}
if ([anotherObject boolValue] == NO) {

}
if (isAwesome == YES) {

} 
if (isAwesome == true) {

}

4.三元操作符的表达,当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。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;

5.在使用 Init方法 和 类构造方法 时需要注意:
Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id

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

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

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS开发-Objective-C语言代码规范-OC代码规范集合-iOS代码规范 的相关文章

  • 无法为 FacebookSDK 4 构建模块“FBSDKCoreKit”

    我在将 Facebook SDK 3 更新到 4 时遇到问题 当我添加框架时 它已成功添加 但是当我添加头文件时出现错误 无法构建模块 FBSDKCoreKit 不知道原因 在设置中 允许非模块化包含在框架模块中 设置为YES在构建设置中解
  • 如何让 iPhone 屏幕变暗

    我的 iPhone 屏幕上有一个刷新按钮 可以刷新当前视图中的表格 屏幕刷新得很漂亮 但是有没有办法让屏幕变暗 然后在表格刷新后再次变亮 您可以在要调暗的视图上放置一个带有黑色背景的非不透明视图 默认情况下 其 alpha 值为 0 因此是
  • 在后台线程上搜索

    我试图在 iPhone 应用程序中搜索数千个对象 但是搜索严重滞后 每次击键后 UI 都会冻结 1 2 秒 为了防止这种情况 我必须在后台线程上执行搜索 我想知道是否有人有一些在后台线程上搜索的提示 我读了一点NSOperation并在网上
  • 在 iPhone 上搜索 PDF

    经过两天尝试使用 Quartz 从 PDF 中读取注释后 我成功做到了并且发布我的代码 https stackoverflow com questions 4080373 get pdf hyperlinks on ios with qua
  • UITableView 在单元格刷新时滚动到顶部

    我有一个 UITableView 其中一个单元格的大小是动态调整的 以适应其中的 UITextView 每当键入一个键时 单元格都会检查计算出的高度是否增加 就像换行符一样 因此它可以告诉表单元格的高度需要重新计算 我用这段代码做到了这一点
  • 带有 Retina-Tiles 的 MKTileOverlay

    我在 MKMapKit 中加载 512x512px 图块时遇到问题 服务器提供 512x512 jpeg 图块 我在 MKMapView 中找不到自定义视网膜图块的任何解决方案或示例实现 我做什么 当我将它们加载到 MKMapView 中时
  • 在 iOS 中,如何以编程方式填写 pdf 表单字段?

    我需要获取一个现有的 pdf 文件 并以编程方式填写带有文本的表单字段列表 然后保存 pdf 而不将其显示给用户 例如 如果 pdf 文件包含名为 LastName 和 FirstName 的字段 我想将 FirstName 的值设置为 L
  • iOS 无法获取人物图像

    我有两个 tableViewController 第一个有联系人列表 另一张显示详细的人员信息 第一个tableViewController的一段代码 ABAddressBookRef addressBook ABAddressBookCr
  • Swift:如何让游戏中的角色只有落地后才能跳跃?

    我正在开发一款游戏 我的角色可以从一个陆地跳到另一个陆地 我已经把所有事情都做好了 除了我剩下的问题是 如果你继续点击屏幕 他可以永远跳跃 我希望他必须先落地才能再次跳跃 import SpriteKit import GameplayKi
  • 如何从 Xcode 4.5 卸载 iOS 5.0 模拟器

    我已经安装了 Xcode 4 5 由于我编写应用程序是为了好玩 我不打算支持旧版 iOS 版本 并且为了节省 500 GB iMac 上的空间 是否有办法删除它 尝试从 Xcode 包中支持的平台中删除 iPhone iPhoneSimul
  • iOS 确定视频中的帧数

    如果我有一个 Swift 中的 MPMoviePlayerController MPMoviePlayerController mp MPMoviePlayerController contentURL url 有没有办法获取视频中的帧数u
  • Swift:Tableview 在导航栏下方滚动但在状态栏上方滚动?

    我使用以下技巧隐藏了导航栏的阴影 self navigationController navigationBar setBackgroundImage UIImage for default self navigationControlle
  • 如何在导航栏上添加 UIView?

    我需要覆盖UINavigationBar with UIView像这儿 除了使用带有按钮返回的自定义 UIView 作为导航栏之外 还有其他方法可以做到这一点吗 您可以将子视图添加到应用程序的基本视图 UIApplication share
  • Apple 由于崩溃而拒绝了我的应用程序,无法重现它

    我刚刚上传了一个应用程序到应用程序商店 它是为ios 7开发的 他们拒绝了该应用程序 因为我无法重现崩溃 他们向我发送了这份崩溃报告 Exception Type EXC BAD ACCESS SIGSEGV Exception Subty
  • 广告标识符和标识符ForVendor返回“00000000-0000-0000-0000-000000000000”

    我已经实现了这个方法来获得advertisingIdentifier and identifierForVendor NSString advertisingIdentifier if NSClassFromString ASIdentif
  • 使用javascript以编程方式触发iOS safari中的复制菜单?

    我正在尝试实现一种用户友好的方式 将一些文本从文本输入字段复制到 iOS Safari 上的剪贴板 我知道无法在这个平台上以编程方式完成此操作 但我希望能够尽可能地指导用户体验 在 iOS Safari 上 当用户手动突出显示某些文本时 会
  • UITableViewCell 内嵌套 UIStackView 内的 UILabel 有时会被截断

    我的一个表设置中有一个表视图单元格 其中包含以下视图层次结构 外部水平 stackview 固定到单元格内容视图的尾部 前部 底部和顶部边缘 右侧标签固定到其父 stackViewHackView 的尾部 前部 底部和顶部边缘 在我的控制器
  • 找不到 Cocoa/Cocoa.h 文件

    我在用XMPPFramework在我的应用程序中 我已将 Cocoa Cocoa h 导入到我的 m 文件中 但是当我构建项目时Xcode显示错误 错误 未找到 Cocoa Cocoa h 文件 我该如何解决这个错误 如果您正在为 iOS
  • iOS 搜索栏不显示结果

    更新 这实际上有效 我的自定义单元格的样式尚未出现 因此单元格看起来是空白的 那我怎样才能得到searchResultsTableView使用我的自定义单元格 我在表格视图中实现了搜索栏 当我调试时搜索 过滤所有工作 但是当我在搜索栏中输入
  • Facebook 登录打开错误的应用程序

    我正在尝试使用 facebook 实现应用程序的登录 但每次我尝试登录时 它都建议打开错误的应用程序 我尝试了一些在这里找到的东西 但没有成功 在 Facebook 的开发者页面上我添加了一个后缀 我的 plist 如下 有谁知道发生了什么

随机推荐