iOS

iOS,第1张

文章目录 1 格式1.1 【必须】代码组织1.2 【推荐】换行1.3 【推荐】函数长度 2 命名2.1 【必须】类和协议名称2.2 【必须】分类2.3 【必须】文件名2.4 【推荐】缩略词和首字母缩写词2.5 【必须】宏定义2.6 【推荐】方法名2.7 【必须】变量与属性名2.8 【推荐】通知和异常 3 注释3.1 【推荐】文件注释3.2 【推荐】声明部分的注释3.3 【推荐】实现部分的注释 4 函数与方法4.1 【必须】基本原则4.2 【必须】修饰4.3 【必须】`nil` 检查4.4 【必须】点语法4.5 【必须】使用轻量级泛型来记录容器的类型4.6 【必须】异常的使用 5 控制结构5.1 【必须】分支结构5.2 【可选】`BOOL` 陷阱 6 类与对象6.1 【必须】明确指定初始化方法、使用指定初始化方法(Designated Initializer)6.2 【必须】重写指定初始化方法6.3 【必须】初始化函数简洁6.4 【必须】保持公共 API 简单 7 Cocoa 相关7.2 【必须】视图布局 8 单测相关8.1、单例的`mock`8.2、测试待Assert的代码: 9 补充:9.1、extern用:FOUNDATION_EXPORT9.2、更新布局9.3、更新subView布局9.4、synthesize/dynamic9.5、判断是否实现了指定协议的方法9.6、IOC:`inversion of control`控制反转9.7、import头文件顺序


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 【必须】修饰

属性的修饰:readonlynonullnullablenull_resettableget不为空,set可为空)、__null_unsepecified(不确定是否为空)

__kindof:当前类 or 其子类

属性:推荐使用上下文相关的非下划线关键字,例如 nonnullnullable
其他场景:推荐使用 _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_BEGINNS_ASSUME_NONNULL_END)或可空性变量修饰符修饰参数。

注:弃用 __nullable__nonnull(苹果为了避免与第三方库潜在的冲突,把 __nullable__nonnull改成了_Nonnull/_Nullable


4.3 【必须】nil 检查

字符串判空:QLSafeString(str) (str?:@"")QNBSafeString(str) (str?str:@"")

nil 检查只用在逻辑流程中,避免逐行代码地在对象发消息前进行 nil 检查。对 nil 发送任何消息都是可以的。

存入NSArrayNSDictionary的数据要判空:!= 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,请使用三元运算符返回 YESNO 值。
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 类与对象

当创建NSStringNSDictionaryNSArray,和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中的相对位置

当访问一个 CGRectxywidthheight 时,应该使用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头文件顺序

自身的头文件
系统库的头文件
开源第三方库的头文件
内部第三方库的头文件
模块内的头文件
项目内的头文件

不同类型的头文件中间最好空行,同类型的头文件尽量按照字母顺序排列

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/996957.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存