GB代码规范
写法模板 #import <系统库> #import <第三方库> #import "其他类" 尽量按照先系统类 第三方类 自己写的类顺序导入 中间不能有空格
建议的写法
#import <UIKit/UIKit.h>
#import <Google/Analytics.h>
#import "GBOrderEmptyView.h"
不建议的写法
#import "GBOrderEmptyView.h"
#import <UIKit/UIKit.h>
#import <Google/Analytics.h>
写法模板 @class class1,class2;
建议的写法
@class UIView,UIImage;
不建议的写法
@class UIPress;
@class UIPressesEvent;
写法模板 @interface 类名 : 父类 <协议1, 协议2> @interface和类名中间一个空格 类名后紧跟:之后空格加上父类协议之间用,空格分割
建议的写法
@interface AppDelegate : UIResponder <UIApplicationDelegate, UITableViewDataSource>
不建议的写法
@interface AppDelegate:UIResponder<UIApplicationDelegate,UITableViewDataSource>
写法的模板 @protocol 协议的名称 <协议1, 协议2> @potocol和协议的名称有空格 协议的名称和其他协议有空格 其他协议之间有空格
建议的写法
@protocol UIResponderStandardEditActions <NSObject>
不建议的写法
@protocol UIResponderStandardEditActions<NSObject>
@property(关键词, 关键词) 类 *变量名称; 关键词用,空格分割 类前后空格
正确写法
@property(strong, nonatomic) UIWindow *window;
不建议的写法
@property (strong, nonatomic) UIWindow * window;
对象 strong 基本变量assign XIB控件 代理 weak 字符串和block使用 copy 对于一些弱引用对象使用weak 对于需要赋值内存对象 copy
写法模板 @interface 方法的参数在一排显示 方法之间保留一行 第一个方法和@interface保留空行 最后一个方法和@end保留空行
建议的写法
@interface Text : NSObject
- (void)testFunction;
@end
错误写法
@interface Text : NSObject
- (void)testFunction;
@end
开头用k标识 推荐k+模板名字首字母大写+作用名称 防止和其他的重复 比如:CartViewModel类需要声明更新购物车列表的通知 kCVMNoticationUpdateCartList 如果是声明Cell的重用字符 k+cell的名称+identifier 比如: GBHomeItemTableViewCell的标识符 kGBHomeItemTableViewCellIdentifier
如果是需要声明在h里面让其他的类用到需要在h声明m实现
声明
UIKIT_EXTERN NSString *const kNoticationUpdateCartList;
实现
NSString *const kNoticationUpdateCartList = @"kNoticationUpdateCartList";
对于如果导入是UIKit类就使用UIKIT_EXTERN 如果是Founction使用关键词FOUNDATION_EXTERN
如果只在本类使用只用写实现 不用写声明。
一个方法内部最多五十行 如果超过就精简代码 就分开方法写
方便之后进行热修复 代码重构
自己管理的类一定注释属性用途 方法的用途 参数的说明
属性如果设置默认值 一定注明默认值是什么
如果方法内部存在逻辑判断 方法跳转 一定注释判断用法 方法跳转用法
除了初始化操作
其他声明变量 赋值 判断 应该注明注释用途
大家平时设置属性默认是可读可写 但是这样容易对于别人造成误解 以为可以赋值
对于只能获取的属性 一定写readonly
头文件引入的类使用@class声明不实用#import引入
可以防止互相引入 编译失败 不容易查找的BUG
造成的缺点
m文件还要#import 其他类调用这个类属性也要#import对应的类
综合来说宁愿自己多操作 也要防止这种循环引入的BUG的出现
对于属性的不同作用 比如设置颜色的 设置字体的 设置其他样式 的可以进行分组
对于方法的作用分类 比如添加功能 删除功能的
对于其他的代理方法 Get Set方法 Init初始化方法
属性set不要带is get要带is
@property(nonatomic, assign, getter=isUserLogin) BOOL userLogin;
不能用init set 开头进行命名
如果不是写初始化方法不要用init进行开头
如果不是属性的set方法不要用set作为方法的前缀
建议的写法
if(YES) {
doing something
}
不建议的写法
if(YES)
{
doing something
}
比如 + - * / =等运算符左右有空格
建议的写法
x = 1 + 2;
不建议的写法
x=1+2;
对于命名一定不要简写 那篇很长的单词 但是一些单词就是简写的除外 比如WTO RMB
UILabel结尾加上Label;
UIImageView结尾记上ImageView
等等让其他的编程人员看名字就知道变量的用法 和属于什么控件
建议的写法
@property(nonatomic, strong) UILabel *userNameLabel;
不建议的写法
@property(nonatomic, strong) UILabel *name;
对于if里面很多的判断条件 要提取出来 方便之后进行断点测试
建议的写法
BOOL isTrue = 5 > 3;
if(isTrue) {
}
不建议的写法
if(5 > 3){
}
对于归属所在的enum 要写在对应的类
我们现在就全部enum放在一个文件 觉得和苹果的编码规范违背 并且分离代码有点麻烦
使用NS_ENUM进行定义
建议的写法
typedef NS_ENUM(NSUInteger, GBAppRunDeveloperMode) {
GBAppRunDeveloperModeDebug,
GBAppRunDeveloperModePreRelease,
GBAppRunDeveloperModeRelease
};
不建议的写法
typedef enum {
GBAppRunDeveloperModeDebug,
GBAppRunDeveloperModePreRelease,
GBAppRunDeveloperModeRelease
}GBAppRunDeveloperMode;
比如UIView的对应初始化方法为
- (instancetype)initWithFrame:(CGRect)frame
UIViewController对应的为
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
防止初始化用init new等没经过系统进行设置一些默认的属性 造成bug
比如系统的 NSNotificationName
typedef NSString *NSNotificationName NS_EXTENSIBLE_STRING_ENUM;
建议的写法
typedef NSString *NSStringConfigProjectName;
FOUNDATION_EXPORT NSStringConfigProjectName const kConfigProjectPluginDebugBaseUrlString;
不建议的写法
FOUNDATION_EXPORT NSString *const kConfigProjectPluginDebugBaseUrlString;
steup with cart buy等等待完善
单词全部的大写 单词之间用_分割
建议的写法
#define NS_AVAILABLE_IOS(_ios) CF_AVAILABLE_IOS(_ios)
不建议的写法
#define NSAvailableIos(_ios) CF_AVAILABLE_IOS(_ios)
建议的写法
[[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]
不建议的写法
[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]
这个我觉得可以不加 但是无法看到苹果的实现 所以不知道苹果的规范怎么写的
建议写法
static NSStringInitCheckManger const KGoogleServerTestKey = @""
不建议的写法
NSStringInitCheckManger const KGoogleServerTestKey = @""
局部的变量要初始化 属性有默认的值 所以我们不必须对于属性进行初始化
我之前遇到的一个BUG就是int类型没有初始化给我默认Nan造成崩溃
建议的写法
int index = 0;
不建议的写法
int index;
对于一些对象判断是否赋值可以不进行初始化 但是对于一定不会为nil要进行初始化
一定要使用驼峰的命名
建议的写法
UNUserNotificationCenter *unCenter = [UNUserNotificationCenter currentNotificationCenter];
不建议的写法
UNUserNotificationCenter *uncenter = [UNUserNotificationCenter currentNotificationCenter];
不要直接调用set方法
建议的写法
unCenter.delegate = self;
不建议的写法
[unCenter setDelegate:self];
建议的写法
UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound
不建议的写法
UNAuthorizationOptionAlert+UNAuthorizationOptionBadge+UNAuthorizationOptionSound
之前研究过很多的第三方的命名 对于苹果官方的没找到
有CallBack结尾 Complete结尾 Block结尾 还有CompletionHandle结尾的
我看到苹果很多的结尾都是用CompletionHandle结尾
大部分命名是Block我们按照Block命名
建议的写法
typedef void(DidUpdateViewCompletionHandle)(void)
错误写法
typedef void(DidUpdateViewCallBack)
因为我们用到NSUserDefaults无非是保存和读取 事先的创建一个对象 可以精简代码
当执行方法很多 用变量替换
建议的写法
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault setDouble:CFAbsoluteTimeGetCurrent() forKey:@"AppStartTime"];
不建议的写法
[[NSUserDefaults standardUserDefaults] setDouble:CFAbsoluteTimeGetCurrent() forKey:@"AppStartTime"]
影响程序的启动
建议的做法
- (void)viewDidLoad {
[super viewDidLoad];
[[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : GBCOLOR(153, 153, 153, 1.0)} forState:UIControlStateNormal];
[[UITabBarItem appearance] setTitleTextAttributes: @{NSForegroundColorAttributeName : GBCOLOR(255, 129, 55, 1.0)} forState:UIControlStateSelected];
}
不建议的做法
+ (void)initialize {
[[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : GBCOLOR(153, 153, 153, 1.0)} forState:UIControlStateNormal];
[[UITabBarItem appearance] setTitleTextAttributes: @{NSForegroundColorAttributeName : GBCOLOR(255, 129, 55, 1.0)} forState:UIControlStateSelected];
}
通知在dealloc要使用移除对象监听的方法
建议的写法
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
不建议的写法
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:name1 object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:name2 object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:name3 object:nil];
}
判断放在一行也是可以的 但是我们还是要求正规一些 毕竟注明Apple的goto BUG
建议的写法
if (!self.startPagesListInfo) {
return ;
}
不建议的写法
if (!self.startPagesListInfo) return ;
定义一下key 方便我们使用 并且方便之后改名字
建议的写法
NSString startLoadString = @"startLoad";
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault objectForKey:startLoadString]
[userDefault setObject:@() forKey:startLoadString]
不建议的写法
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
[userDefault objectForKey:@"startLoad"]
[userDefault setObject:@() forKey:@"startLoad"]
建议的写法
- (BOOL)judgeStartLoadPageInTimeCurrentWithModel:(StartPageModel *)obj
不建议的写法
- (BOOL)judgeStartLoadPageInTimeCurrentWithModel : (StartPageModel *)obj;
建议的写法
@weakify(self);
[@[] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
@strongify(self);
}];
不建议的写法
__weak typeof(self) weakSelf = self;
[@[] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
__strong typeof(weakSelf) strongSelf = weakSelf;
}];
可以实现GBInitViewProtocol协议 执行对应的方法
建议的写法
#pragma mark - <GBInitViewProtocol>
- (void)gbInitView {
self.backgroundColor = GBCOLOR_WHITE;
[self addSubview:self.loadImageView];
}
- (void)gbAutoLayoutView {
[self.loadImageView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.mas_equalTo(self).insets(UIEdgeInsetsZero);
}];
}
有利于其他人很方便查找当前界面布局和添加试图的位置
我们一个界面有很多控件 利用懒加载可以美化代码
所有的懒加载放在Getter的mark的下面
建议的写法
#pragma mark - getter
- (UIImageView *)loadImageView {
if (!_loadImageView) {
_loadImageView = [[UIImageView alloc] init];
_loadImageView.image = [self imageWithScreen];
_loadImageView.contentMode = UIViewContentModeScaleAspectFill;
_loadImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(loadImageTouchAction)];
[_loadImageView addGestureRecognizer:tap];
}
return _loadImageView;
}
所有界面的控件元素独立到另外的UIView
UIViewController只负责跳转界面
新建UIView负责界面的显示
VIewModel负责数据的请求和解析
APi负责请求
model负责后台数据解析
other 负责样式和其他处理
字符串 @""
NSString *string = @"string";
NSNumber @()
NSNumber *number = @(1);
字典 @{}
NSDictionary *dictionary = @{@"name":@"123"};
数组 @[]
NSArray *array = @[@"321231",@"123"];
字典和数组的取值和存值
建议的写法
static const NSTimeInterval kAnimationDuration = 0.3;
不建议的写法
#define ANIMATION_DURATION 0.3
尽量少用根据数字判断状态少用字符串 数字判断状态
建议的写法
typedef NS_ENUM(NSUInteger, HomeViewState) {
HomeViewStateNoData,
HomeViewStateFailure,
HomeViewStateItemList,
HomeViewStateBannerList
};
if(state == HomeViewStateNoData){
// 显示没数据
}else if(state == HomeViewStateFailure) {
// 显示请求错误
}else if(state == HomeViewStateItemList) {
// 显示商品的列表
}else if(state == HomeViewStateBannerList) {
// 显示banner列表
}
不建议的写法
if(state == 0){
// 显示没数据
}else if(state == 1) {
// 显示请求错误
}else if(state == 2) {
// 显示商品的列表
}else if(state == 3) {
// 显示banner列表
}
比如我们需要创建一个类 有多个样式
typedef NS_ENUM(NSUInteger, ZHCustomViewStyle) {
ZHCustomViewStyleRed,
ZHCustomViewStyleWhite
};
@interface ZHCustomView : UIView
+ (instancetype)customWithStyle:(ZHCustomViewStyle)style;
@end
#import "ZHCustomView.h"
#import "ZHCustomRedView.h"
#import "ZHCustomWhiteView.h"
@implementation ZHCustomView
+ (instancetype)customWithStyle:(ZHCustomViewStyle)style {
switch (style) {
case ZHCustomViewStyleRed: {
return [[ZHCustomRedView alloc] initWithFrame:CGRectZero];
}
break;
case ZHCustomViewStyleWhite:{
return [[ZHCustomWhiteView alloc] initWithFrame:CGRectZero];
}
break;
default:
break;
}
}
@end
ZHCustomRedView
#import "ZHCustomView.h"
@interface ZHCustomRedView : ZHCustomView
@end
#import "ZHCustomRedView.h"
@implementation ZHCustomRedView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
@end
ZHCustomWhiteView
#import "ZHCustomView.h"
@interface ZHCustomWhiteView : ZHCustomView
@end
#import "ZHCustomWhiteView.h"
@implementation ZHCustomWhiteView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor whiteColor];
}
return self;
}
@end
因为团队的合作 可能会出现大家想到一样的名字或者添加第三方库引入和第三方库名字一样
尽量加上前缀。
如果只针对工程就使用工程的缩写
比如自己个人的第三方库就加上自己名字或者昵称的缩写
建议的写法
@interface GBHomeViewController : NSObject
不建议的写法
@interface HomeViewController : NSObject
对于初始化参数有很多 但是不是一定全部使用的可以提供多个初始化方法
建议的写法
- (instancetype)initWithFrame:(CGRect)frame;
- (instancetype)initWithPerson:(GBPersonModel *)person;
- (instancetype)initWithFrame:(CGRect)frame person:(GBPersonModel *)person;
不建议的写法
- (instancetype)initWithFrame:(CGRect)frame person:(GBPersonModel *)person;
这个不推荐自己手写 可以使用Xcode插件自动生成 属性越多会加重手写代码的长度
对于OC存在很多可变的对象 比如NSMutableString NSMutableArray NSMutableDictionary等等
对于一些不允许改变的直接使用不可变对象
可以节省对象开支 还可以防止别人修改数据造成bug
建议的写法
NSArray *sexList = @[@"男",@"女"];
不建议的写法
NSMutableArray *sexList = [NSMutableArray arrayWithArray:@[@"男",@"女"]]
我觉得代理可以用在写控件需要数据源赋值 和一些事件回调的时候使用
我查阅了苹果的block基本上都是执行一个时间 需要异步回调就使用block
如果没有主动执行动作 而是监听异步的回调 建议用代理
建议的写法
TestName *name = [[TestName alloc] init];
name.delegate = self;
[name searchText:text completionHandle:^(BOOL isExit) {
};
- (void)name:(TestName)name resultTextHaveChanged:(NSString *)text {
}
不建议的写法
TestName *name = [[TestName alloc] init];
[name searchText:text completionHandle:^(BOOL isExit) {
};
name.resultTextHaveChanged = ^(NSString *text) {
};
记得在Dealloc释放注册的通知和KVO的监听
不释放容易造成内存释放崩溃
对于一些有按照功能类型的方法划分在一个分类里面 分类和之前类写在同一个文件
建议的写法
@interface GBPerson : NSObject
@end
@interface GBPerson (Friend)
// 朋友
- (void)addFriend:(GBPenson *)friend;
- (void)deleteFriend:(GBPenson *)friend;
@end
@interface GBPerson (Play)
// 娱乐
- (void)playSound;
- (void)playGame;
@end
不建议的写法
@interface GBPerson : NSObject
// 朋友
- (void)addFriend:(GBPenson *)friend;
- (void)deleteFriend:(GBPenson *)friend;
// 娱乐
- (void)playSound;
- (void)playGame;
比如为系统UIView添加分类Add的添加前缀
建议的写法
@interface UIView (GB_Add)
- (void)gb_addCustomView:(CustomView *)customView;
@end
不建议的写法
@interface UIView (Add)
- (void)addCustomView:(CustomView *)customView;
@end
假设我们分类有一个只读的字段 我们可以不使用属性 可以使用方法
建议的写法
@interface UIView (Add)
- (NSArray *)customViews;
@end
@implementation UIView (Add)
- (NSArray *)customViews {
return @[customView1,customView2];
}
@end
不建议的写法
@interface UIView (Add)
@property(nonatomic, strong, readonly) NSArray *customViews;
@end
@implementation UIView (Add)
- (NSArray *)customViews {
return @[customView1,customView2];
}
@end
可以利用主类的私有变量
建议的写法
@interface ZHCustomView : UIView
@end
@interface ZHCustomView (Add)
@property(nonatomic, copy) NSString *name;
@end
#import "ZHCustomView.h"
@implementation ZHCustomView {
NSString *_name;
}
@end
@implementation ZHCustomView (Add)
- (void)setName:(NSString *)name {
_name = name;
}
- (NSString *)name {
return _name;
}
@end
不建议的写法
- (void)setName:(NSString *)name {
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
return objc_getAssociatedObject(self, "name");
}
对于给第三方和系统的类非要添加属性 可以使用runtime。
对于不知道后台返回什么类型的 可以使用try catch
建议的写法
int index = 0;
@try {
NSArray *array = obj[@"list"];
index = [array.firstObject intValue];
}
@catch {}
因为OC是运行时语法 可能array不一定是NSArray类型的
不建议的写法
int index = 0;
NSArray *array = obj[@"list"];
if(array.count > 0) {
index = [array.firstObject intValue];
}
如果后台返回list为字段 这段代码就崩溃了 可以使用try catch也可以用Model库 或者自己添加判断
建议的写法
+ (instancetype)sharedInstance {
static ZHCustomView* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [ZHCustomView new];
});
return instance;
}
不建议的写法
+ (instancetype)sharedInstance {
static ZHCustomView* instance = nil;
if(!instance) {
instance = [[ZHCustomView alloc] initWithFrame:CGRectZero];
}
return instance;
}
如果只需要便利数组和字典的写法用for in
建议的写法
for(NSString *name in names) {
//
}
不建议的写法
for(int i = 0; i < names.lenght ; i ++) {
NSString *name = names[i];
}
需要便利字典和数组的内容 并且需要索引用enumerator
建议的写法
[names enumerateObjectsUsingBlock:^(NSString * _Nonnull name, NSUInteger idx, BOOL * _Nonnull stop) {
}];
不建议的写法
for(int i = 0; i < names.lenght ; i ++) {
NSString *name = names[i];
}
建议的写法
NSCache *cache = [[NSCache alloc] init];
[cache setObject:object forKey:key];
不建议的写法
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[dictionary setObject:object forKey:key];
推荐:
if ([myValue isEqual:@42]) { ...
不推荐:
if ([@42 isEqual:myValue]) { ...
推荐
if(name) {
}
if (isMyFriend) {
}
不建议的写法
if(name != nil) {
}
if(isMyFriend == YES) {
}
建议的写法
if(name.lenght <= 0) {
return;
}
if(![name isEqualToString:@"zhanghang"]) {
return;
}
.....
不建议的写法
if(name.lenght > 0) {
if([name isEqualToString:@"zhanghang"]) {
....
}
}
建议的写法
BOOL nameContainsSwift = [sessionName containsString:@"Swift"];
BOOL isCurrentYear = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession = nameContainsSwift && isCurrentYear;
if (isSwiftSession) {
// Do something very cool
}
不建议的写法
if ([sessionName containsString:@"Swift"] && [sessionDateCompontents year] == 2014) {
// Do something very cool
}
推荐:
result = a > b ? x : y;
不推荐:
result = a > b ? x = c > d ? c : d : y;
当三元运算符的第二个参数(if 分支)返回和条件语句中已经检查的对象一样的对象的时候,下面的表达方式更灵巧:
推荐:
result = object ? : [self createObject];
不推荐:
result = object ? object : [self createObject];
有些方法通通过参数返回 error 的引用,使用这样的方法时应当检查方法的返回值,而非 error 的引用。
推荐:
NSError *error = nil;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
此外,一些苹果的 API 在成功的情况下会对 error 参数(如果它非 NULL)写入垃圾值(garbage values),所以如果检查 error 的值可能导致错误 (甚至崩溃)。
建议的写法
NSArray<NSString *> *names = [NSArray array];
不建议的写法
NSArray *names = [NSArray array];
建议的写法
NSArray *array = @[
@"a",
@"b",
@"b"
];
NSDictionary *dictionary = @{
@"a":@"",
@"b":@"",
@"c":@""
};
不建议写法
NSArray *array = @[@"a",@"b",@"b"];
NSDictionary *dictionary = @{@"a":@"",@"b":@"",@"c":@""};