首选Swift,除非部分需要使用OC的场景:
- 基础组件(需要兼容OC调用)
- 需要和C/C++交互
文件命名
类
- 文件使用
PG
前缀, 注意大写
. - 业务组件需要添加模块前缀, 比如
Order
/HomePage
.
PGOrderViewController.h
/ PGHomePageViewController.m
提示: 避免潜在的命名冲突.
分类
- 分类使用
类名
+扩展字段
@Interface NSObject(Test)
@property (nonatomic) NSInteger pg_num;
- (void)pg_test;
@end
UIView+Addition.h
/ UIView+Addition.m
常见问题
关于文件命名缩写
比如Button
使用Btn
, ViewController
使用VC
.
个人建议不要
使用缩写, 缩写会增加理解的难度.除非是一些大家都知道的缩写方式, 比如HTTP
/DNS
.
属性
@property (nullable, nonatomic, readonly, strong) NSObject *object;
@property (nonatomic, readonly, assign) NSInteger num;
@property (nonatomic, readonly) NSInteger num2;
- 添加必要的
空格
, 属性修饰符保持固定的顺序. 保持统一的风格提高可读性
提示: 可以考虑不添加
assign
, 基础类型默认assign
. 参考Apple
官方API
使用_
替代self
读写属性值
self.num = 1;
_num = 1;
- 性能更好, 不需要调用set/get方法
提示: 除非你需要使用set/get方法.
多使用readonly
属性
- 外部不可修改只读, 更安全
提示: 当你公开读权限的时候, 需要假定这个值会被修改, 而不是你知道这个值没有人会去修改.
不推荐使用实例变量
@interface ViewController () {
NSString *name;
}
@end
- 无法添加属性相关修饰.
提示: 如果不想生成get/set方法, 可以添加@dynamic标示.
### `常见问题`
##### `init`方法内使用`self`,而不是`_`
- (instancetype)init {
if (self) {
self.num = 1; // 错误
_num = 1;
}
return self;
}
##### `copy`/`strong`错误使用
```objc
@property (nonatomic, readonly, strong) NSArray *array; // error
@property (nonatomic, readonly, strong) NSString *array; // error
@property (nonatomic, readonly, copy) NSMutableArray *array; // error
- (NSArray *)elements {
// return [self.mutableElements copy];
return self.mutableElements; // error
}
外部不可修改的属性不添加readonly
提示: 应该尽可能减少对外公开的权限, 减少潜在的bug
没有实现set/get方法, 使用self
而不是_
方法
分类方法添加前缀
- (void)pg_method {
}
- (NSString *)method:(NSString *)string {
return @"";
}
- 良好的
方法
/参数
命名 - 添加适当的空格增加
可读性
推荐使用.
语法代替[]
获取类属性
推荐
NSUserDefaults.standardUserDefaults
mutableElements.copy
不推荐
[NSUserDefaults standardUserDefaults]
[mutableElements copy]
- 减少嵌套, 代码更简洁
使用instancetype
代替id
常见问题
使用id
而不是instancetype
使用extern
修饰对外开放的常量
//Object.h
extern NSString *const String;
//Object.m
NSString *const String = @"Finish Download";
字面值
推荐
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @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 *buildingZIPCode = [NSNumber numberWithInteger:10018];
使用NS_ENUM
基础类型枚举使用NS_ENUM
typedef NS_ENUM(NSUInteger, PGOrderCashbackStatus) {
PGOrderCashbackStatusPay = 1,
PGOrderCashbackStatusWait = 2,
};
- 更好的兼容
swift
let status: PGOrderCashbackStatus = .pay
字符串枚举使用NS_STRING_ENUM
typedef NSString *ViewControllerKey NS_STRING_ENUM;
FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeyTitle;
FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeySubtitle;
FOUNDATION_EXPORT ViewControllerKey const ViewControllerKeySummary;
- 更好的兼容
swift
let type = ViewControllerKey.Title
集合
推荐
NSString *test = dictionary[@"test"];
不推荐
NSString *test = [dictionary objectForKey:@"test"];
使用initWithCapacity
[[NSDictionary alloc] initWithDictionary:2];
[[NSMutableArray alloc] initWithCapacity:2];
- 当容易有明确大小时, 使用
使用initWithCapacity
创建
提示: 避免之后需要扩容, 提高性能, 减少内存消耗
添加nullable
NS_ASSUME_NONNULL_BEGIN
@property (nullable, nonatomic, readonly) UIView *superview;
- (CGPoint)convertPoint:(CGPoint)point toView:(nullable UIView *)view;
- (nullable __kindof UIView *)viewWithTag:(NSInteger)tag;
NS_ASSUME_NONNULL_END
对应的swift代码
var superView: UIView?
func convertPoint(point: CGPoint, view: UIView?) -> CGPoint {}
func viewWithTag(tag: Int) -> UIView?
添加NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END
.
- 默认都为
nonnull
, 不为空
提示: 主要是针对老文件, Xcode新版本会自动添加
- Xcode会做静态检查, 比如给一个不可为空的
参数/属性
传递nil值会warning
- 更好的兼容
swift
常见问题
不添加NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
- 如果没有添加
NS_ASSUME_NONNULL_BEGIN
/NS_ASSUME_NONNULL_END
, swift会直接当做强解包处理, 如果属性为nil
会导致crash
属性/参数/返回值可能为nil
, 不添加nullable
标示.
添加NS_NOESCAPE
NS_NOESCAPE
用于闭包参数声明, 保证函数内部不会持有该闭包, 不会有循环依赖
的风险.
- (NSArray *)makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *))block {
return [self mas_makeConstraints:block];
}
- 默认编译器会当做逃逸闭包处理
性能提升
编译器会做优化, 比如省略非必要的对
self
的捕获/保留/释放.
- 更好的兼容
swift
扩展: Apple @noescape提案
添加泛型
集合
@property(nonatomic,copy) NSArray<__kindof UIViewController *> *viewControllers;
NSDictionary *dictionary;
- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;
当NSArray
/NSDictionary
/NSSet
容器中的元素有明确
的类型时, 添加泛型
标识
__kindof
__kindof
用来标识所有符合的类
和子类
都可以匹配到
-
Xcode
可以做一些简单的编译检查, 当类型不一致
时发出警告
NSMutableArray *strings = [NSMutableArray arrayWithCapacity:2];
[strings addObject:@""];//
// Xcode警告 Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString * _Nonnull'
[strings addObject:@(2)];
// Xcode警告 Incompatible pointer types initializing 'UIView *' with an expression of type 'NSString *'
UIView *view = strings[0];
- 更好的兼容
swift
不添加泛型标识, 在swift中为Any.
基础类型
NSInteger
尽可能使用NSInteger代替int
/long
, 除非必须.
BOOL
使用YES
/NO
, 不要使用true
/false
扩展链接: ObjC的BOOL为什么要用YES、NO而不建议用true、false?
宏
#define String @""
- 尽可能减少宏的使用, 不兼容
swift
import
不要import组件的umbrella.h
头文件
#import // 错误
- 会导致引入更多头文件
- 降低编译速度
- 导致部分文件间接依赖某些头文件, 导致部分宏冲突
- 尽可能
import
明确的的文件.h
#import
提示: 特别是头文件中, 尽可能减少
import
减少头文件import导入
- 部分#import使用@class代替
提示: 避免导入更多头文件, 提高编译速度
推荐使用@import代替#import
添加#pragma mark -
@implement UIViewController ()
#pragma mark - LifeCycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
#pragma mark - Private
- (void)test {}
#pragma mark - Getter
- (NSInteger)num {
return 1;
}
@end
- 添加
#pragma mark
分割同一个文件中的不同模块 - 有利于
Xcode Minimap
提示
NS_ERROR
Handling Cocoa Errors in Swift
使用NSNotificationName
UIKIT_EXTERN NSNotificationName const UIApplicationWillEnterForegroundNotification
NSNumber
使用BOOL/NSInteger代替NSNumber
- 大部分场景并不需要使用到
NSNumber
对象, 值类型比对象性能更好. 除非你需要使用引用语义
.
使用@()创建NSNumber/NSString
推荐
@(error.code)
@(error.code).stringValue
不推荐
[NSString stringWithFormat:@"%ld", (long)error.code]
不推荐使用+load
/hook
方法
尽可能避免使用+load
或hook
系统库相关的方法
提示: 如果一定要使用, 在iOS开发群里说一下. 并且在gitlab组件
readme.md
中备注.
总结
- 尽可能规范代码风格, 保持一致的风格, 提高
可读性
.
扩展阅读: Effective Objective-C 2.0
扩展阅读: 禅与 Objective-C 编程艺术
扩展阅读: WWDC-Refine Objective-C frameworks for Swift
扩展: 关于命名和代码风格, 可以多看看iOS系统官方的库, 和一些开源库, 比如
AFNetworking
/SDWebImage
/React Native
.
待更新:推荐使用class属性代替方法