iOS_Objective-C编程规范
1 格式
1.1 【必须】代码组织
- 使用
#pragma mark -
将各 protocol 实现函数、功能相近的函数分组排放。 - 函数定义前空一行。
#pragma mark - Initial Methods
#pragma mark - Override Methods
#pragma mark - Private Methods
#pragma mark - Public Methods
#pragma mark - Notifications
#pragma mark - Event Handlers
1.2 【推荐】换行
- 一行代码不应超过
150 个字符
,超过应该换行。 - 豁免场景:不计算字符串内容的长度。
- (id<UIAdaptivePresentationControllerDelegate>)
adaptivePresentationControllerDelegateForViewController:(UIViewController *)viewController;
- (void)presentWithAdaptivePresentationControllerDelegate:
(id<UIAdaptivePresentationControllerDelegate>)delegate;
1.3 【推荐】函数长度
如果一个函数除空行和注释以外的内容超过了80 行
,则可以思考,能否在不破坏程序结构的前提下,对函数进行拆分。
2 命名
2.1 【必须】类和协议名称
驼峰式命名:Upper camel case
类名:应该包含一个名词,该名词能清楚的表明类(或类的对象)的描述或者行为。跨应用使用的类和协议必须使用合适的前缀(例如:GTMSendMessage)。
协议名:通用的方式是使用动名词来命名协议。例如:NSLocking
2.2 【必须】分类
-
分类名称前缀,表明分类属于哪个项目或模块,如
NSString (GTMParsing)
-
分类的方法前缀,避免和系统库/其他项目/其他模块的方法名称冲突,如
gtm_myCategoryMethodOnAString:
2.3 【必须】文件名
文件的扩展名及其意义如下:
.h C/C /Objective-C 的头文件
.m Objective-C 实现文件
.mm Objective-C 实现文件
.hpp C 头文件
.cpp 纯 C 的实现文件
.c 纯 C 的实现文件
2.4 【推荐】缩略词和首字母缩写词
alloc:分配、dealloc:销毁、alt:轮流,交替、calc:计算
pboard:粘贴板(仅对常量)、horiz:水平 、vert:竖直
init:初始化、func:函数、msg:消息、info:信息、rect:矩形
Temp:临时、暂时、nib:interface builder 文档
计算机行业中存在一些首字母缩写词,推荐全大写(优先级高于驼峰命名法!!!)。常见的一些首字母缩写词如下:
ASCII、PDF、XML、HTML、URL、RTF、HTTP、TIFF
JPG、PNG、GIF、LZW、ROM、RGB
CMYK、MIDI、FTP、JSON、OS、ID
2.5 【必须】宏定义
宏命名请使用蛇式命名:shouty snake case
,将全部字母大写并合理使用下划线分割单词。同时,类 C 函数风格的命名也是允许的。
#define QQ_DEBUG_BUILD ... // GOOD
#define QQ_ASSERT_GT(X, Y) ... // GOOD, 宏风格
#define QQAssertGreaterThan(x, y) ... // GOOD, 函数风格,参数遵循驼峰命名
#define kIsDebugBuild ... // AVOID
#define unless(X) if(!(X)) // AVOID
对于 Xcode 生成的头文件,默认会生成以#define filename_h
命名的宏来防止多重包含。如:
// QQSharedDefine.h
#ifndef QQSharedDefine_h // 该宏来防止多重包含
#define QQSharedDefine_h
...
#endif /* QQSharedDefine_h */
2.6 【推荐】方法名
返回布尔值的 getter 命名应以 is/can/should 等开头,但属性名不应包含 is/can/should。
@property (nonatomic, getter=isGlorious) BOOL glorious;
- (BOOL)isGlorious;
BOOL isGood = object.glorious; // GOOD.
BOOL isGood = [object isGlorious]; // GOOD.
BOOL isGood = object.isGlorious; // AVOID.
2.7 【必须】变量与属性名
-
局部变量
和属性
命名首字母小写,采用驼峰命名法。 -
文件范围
或全局变量
使用g
作为前缀!!!
static int gGlobalCounter;
- 常量(
const
全局和静态变量)应使用驼峰命名法,不要使用#define
宏来定义常量。
整型常量,尽量使用const
或者枚举;浮点型常量,使用const
定义。
错误处理需要定义常量时,推荐使用错误相关的类型 NSErrorDomain
和错误相关的枚举宏 NS_ERROR_ENUM
:
extern NSErrorDomain const QQServiceErrorDomain;
NS_ERROR_ENUM(QQServiceErrorDomain) {
QQServiceErrorFileNotFound = -9000,
QQServiceErrorTimeout = -9001,
};
- 枚举使用
NS_ENUM
。
typedef NS_ENUM(NSInteger, QzoneFeedType) {
QzoneFeedTypeFriends = 0,
QzoneFeedTypeHomepage,
QzoneFeedTypeBlog,
};
- 位掩码使用
NS_OPTIONS
。
typedef NS_OPTIONS(NSUInteger, NYTAdCategory) {
NYTAdCategoryAutos = 1 << 0,
NYTAdCategoryJobs = 1 << 1,
NYTAdCategoryRealState = 1 << 2,
NYTAdCategoryTechnology = 1 << 3
};
2.8 【推荐】通知和异常
-
通知使用
NSNotificationName
作为类型,常量标识,其名称以这种方式组成:[Name of associated class] [Did | Will] [UniquePartOfName] Notification
UIKIT_EXTERN NSNotificationName const NSApplicationDidBecomeActiveNotification
UIKIT_EXTERN NSNotificationName const NSWindowDidMiniaturizeNotification
- 异常名称由全局
NSString
对象标识,以这种方式组成:[Prefix] [UniquePartOfName] Exception(每部分首字母大写)
NSColorListIOException
NSColorListNotEditableException
NSDraggingException
3 注释
3.1 【推荐】文件注释
必须包含文件名,作者,创建时间,版权等信息,可以使用 Xcode 工程的默认模板。对文件内容的基本描述。
// QQObj.h
// 消息对应的数据结构
// Created by NAME on 2019/07/30
// Copyright (c) 2019年 Tencent. All rights reserved.
//
3.2 【推荐】声明部分的注释
函数接口应加以注释,以描述函数功能与参数定义,以及其他模块,文件的关系。属性,成员变量,协议等的声明必要时要加上注释。
如果已经在文件头部详细描述了接口,可以直接说明 “完整的描述请参见文件头部”。
对外暴露的所有接口都应该有注释来解释它的作用、参数、返回值。
对外暴露的接口应该在注释中说明线程安全性
。如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。
注:接口设计需要经可能的便于UT !!!(不要写无参数无返回值的接口)
3.3 【推荐】实现部分的注释
重要或复杂逻辑必须加上注释。
// Set the property to nil before invoking the completion handler to
// avoid the risk of reentrancy leading to the callback being
// invoked again.
CompletionHandler handler = self.completionHandler;
self.completionHandler = nil;
handler();
行尾注释应与代码分开至少 2 个空格,并保持对齐。
[self doSomethingWithALongName]; // Two spaces before the comment.
[self doSomethingShort]; // More spacing to align the comment.
4 函数与方法
4.1 【必须】基本原则
-
参数个数越少越好,多于
6 个参数
时建议考虑重构。 -
函数的边界(参数的要求、返回值的范围、是否返回为空)要在注释中写明,且在代码中明确检查,包括断言及
if
判断。 -
事件方法要写参数(如:xxxx:(UIButton *)sender )
4.2 【必须】修饰
属性的修饰:readonly
、nonull
、nullable
、null_resettable
(get
不为空,set
可为空)、__null_unsepecified
(不确定是否为空)
__kindof
:当前类 or 其子类
属性:推荐使用上下文相关的非下划线关键字,例如 nonnull
和 nullable
。
其他场景:推荐使用 _Nullable
和 _Nonnull
关键字。
NS_ASSUME_NONNULL_BEGIN // Nonnull Audited Regions
@interface MOClass ()
// 声明属性修饰(必须)
@property (nonatomic, copy, readonly, nullable) NSString *aString;
@property (nonatomic, copy, readonly) NSString * _Nullable aString;
// 方法 返回值 和 参数 修饰(必须)
- (nullable NSString *)methodWithString:(nullable NSString *)aString;
- (NSString * _Nullable)methodWithString:(NSString * _Nullable)aString;
NSArray<GTMBook *> *_Nullable GTMLoadBooksFromFile(NSString *_Nonnull path);
@end
NS_ASSUME_NONNULL_END
可以使用区域设置( NS_ASSUME_NONNULL_BEGIN
和 NS_ASSUME_NONNULL_END
)或可空性变量修饰符修饰参数。
注:弃用 __nullable
和 __nonnull
(苹果为了避免与第三方库潜在的冲突,把 __nullable
和 __nonnull
改成了_Nonnull
/_Nullable
)
4.3 【必须】nil
检查
字符串判空:QLSafeString(str) (str?:@"")
、QNBSafeString(str) (str?str:@"")
nil
检查只用在逻辑流程中,避免逐行代码地在对象发消息前进行 nil
检查。对 nil
发送任何消息都是可以的。
存入NSArray
和NSDictionary
的数据要判空:!= nil && != NULL
4.4 【必须】点语法
建议使用点语法来访问或者修改 OC 类的属性,访问其他 OC 方法时首选方括号方式。
init
相关方法和 dealloc
里面不要用点语法!!!
4.5 【必须】使用轻量级泛型来记录容器的类型
// 使用 Xcode 7 及以上版本的所有项目都应该使用 Objective-C 轻量级泛型表示法来表明容器包含的对象。
@property (nonatomic, copy) NSArray<Location *> *locations;
@property (nonatomic, copy, readonly) NSSet<NSString *> *identifiers;
NSMutableArray<Location *> *mutableLocations = [otherObject.locations mutableCopy];
// 如果类型比较复杂,请考虑使用 typedef 来保持可读性。
typedef NSSet<NSDictionary<NSString *, NSData *> *> TimeZoneMappingSet;
TimeZoneMappingSet *timeZoneMappings = [TimeZoneMappingSet setWithObject:...];
// 如果类型不确定,使用 id 来声明。
@property (nonatomic, copy) NSArray<id> *unknowns;
4.6 【必须】异常的使用
(1)可以使用 @try/@catch/@finally/@throw
来进行异常处理。
(2)也可以通过返回值(nil
, NULL
, NO
或者 错误码
)
(3)或者传递一个 NSError
对象来返回错误。
鉴于使用异常的代价较高(安装包、退堆栈带来的性能开销,此外还可能引发内存泄露),条件允许时,应该优先使用 NSError
对象或者返回错误码形式,但对于第三方组件的代码,在使用时,应使用 @try/@catch
进行异常保护
对于后台返回的数据以及文件中读取的数据,应进行足够的校验与异常保护。包括但不限于对数据类型、长度进行校验,使用 @try/@catch
进行序列化,反序列化过程的保护等。
5 控制结构
5.1 【必须】分支结构
if-else
结构不能超过四层
。- 条件分支中最快路径代码要放在最前面,可以有多个
return
。 - 所有的
for
,if
,while
等语法结构主体都必须用花括号,即使主体代码只有一行。
5.2 【可选】BOOL
陷阱
- 将常规整数值转换为
BOOL
,请使用三元运算符返回YES
或NO
值。
对BOOL
使用逻辑运算符 (&&
,||
和!
) 是可以的,其返回值可以安全转换为BOOL
,无需三元运算符。
- (BOOL)isBold {
return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;
}
- (BOOL)isValid {
return [self stringValue] != nil;
}
- (BOOL)isEnabled {
return [self isValid] && [self isBold];
}
// AVOID:
- (BOOL)isBold {
return [self fontTraits] & NSFontBoldTrait; // AVOID.
}
- (BOOL)isValid {
return [self stringValue]; // AVOID.
}
- 永远不要直接将
BOOL
变量与YES
比较,返回值可能不如你所愿。BOOL
定义为signed char
,因此它可能具有除YES
(1
) 和NO
(0
) 之外的值。也没有必要将 BOOL 值与 NO 比较,使用if
以及!
进行判断会使代码更为直观。
BOOL great = [foo isGreat];
if (great) { } // GOOD.
if (![someObject boolValue]) {} // GOOD.
// AVOID:
BOOL great = [foo isGreat];
if (great == YES) { } // AVOID. 永远别这么做
if ([someObject boolValue] == NO) { } // AVOID
6 类与对象
当创建NSString
, NSDictionary
,NSArray
,和NSNumber
类的不可变实例时,都应该使用字面量。
6.1 【必须】明确指定初始化方法、使用指定初始化方法(Designated Initializer)
6.2 【必须】重写指定初始化方法
对于需要继承你的类的人来说,明确指定初始化方法十分重要。这样他们就可以只重写一个初始化方法(可能是几个)来保证他们的子类的初始化方法会被调用。这也有助于将来别人调试你的类时,理解初始化代码的工作流程。
// 禁用 无效的 初始化方法
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCoder NS_UNAVAILABLE;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
// 指定初始化方法
- (instancetype)initWithFrame:(CGRect)frame
type:(NSInterger)type NS_DESIGNATED_INITIALIZER;
6.3 【必须】初始化函数简洁
6.4 【必须】保持公共 API 简单
7 Cocoa 相关
7.2 【必须】视图布局
-
避免在界面布局中使用magic number,应使用能够说明用途的常量。
-
建议在界面布局时使用相对布局,例如:
- 使用目标
view
在父view
中的相对位置 - 使用目标
view
与相关view
中的相对位置 - 使用目标
view
与相邻view
中的相对位置
- 使用目标
-
当访问一个
CGRect
的x
,y
,width
,height
时,应该使用CGGeometry
函数代替直接访问结构体成员。
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
8 单测相关
8.1、单例的mock
不能直接mock
单例的,会引起mock
冲突。
推荐的写法:
id center = OCMPartialMock([[QLLoginCenter alloc] init]); // 每次mock alloc 一个单例
OCMStub([[center classMethod] sharedInstance]).andReturn(center); // mock 它的 sharedInstance 方法
8.2、测试待Assert的代码:
BOOL executed = NO;
@try {
executed = YES;
NSUInteger invalidCount = [vc numberOfUnreadMessagesWithID:@"invalidBlockID"];
XCTAssertEqual(invalidCount, 0);
} @catch (NSException *exception) {
XCTAssertNotNil(exception);
}
9 补充:
9.1、extern用:FOUNDATION_EXPORT
9.2、更新布局
这个不可以直接调用layoutSubviews
,可以用setNeedLayout
,如果等不到下一次刷新可以调用layoutIfNeeded
9.3、更新subView布局
- (void)layoutSubviews {
[super layoutSubviews];
// TODO
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (button.superview == self.view) {
[button sizeToFit]; // 可以这样获得自适应size
}
}
9.4、synthesize/dynamic
// 系统默认实现
@synthesize propertyName = _propertyName;
// @dynamic 阻止自动合成
9.5、判断是否实现了指定协议的方法
[MOClass conformsToProtocol:@protocol(MOLockingProtocol)];
[vc conformsToProtocol:@protocol(MOLockingProtocol)];
9.6、IOC:inversion of control
控制反转
如:Cell
持有VM
,但是VM
不持有Cell
;当VM
需要通知Cell
更新时,可以先注册Block
,在需要时调用就好,就不会导致互相依赖这样高耦合的代码,即控制反转。
9.7、import头文件顺序
自身的头文件
系统库的头文件
开源第三方库的头文件
内部第三方库的头文件
模块内的头文件
项目内的头文件
不同类型的头文件中间最好空行,同类型的头文件尽量按照字母顺序排列
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhjiia
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01