代码规范
编码规范
说明:以下总结是个人从工作经验中所学总结,和实际项目保持大致,分格式化代码和文件结构二大类;为了不影响阅读,一个类的代码行数尽量不要超过300行;一个方法尽量不要超过30行。有超过的在重构的时候想办法分解。
一.格式化代码(所有代码书写规范要和苹果官方优美的api保持一致)
1.在面向特定应用的代码中,类名应尽量避免使用前缀。
2.类名的首字母大写,写使用首字母大写的形式 分割单词;
3.方法名的首字母小写,且使用首字母大写的形式分割单词。方法的参数使用相同的规则。
4.方法名+参数应尽量读起来像一句话(如:)。在这里查看苹果对方法命名的规范;
5.变量名应使用容易意会的应用全称,且首字母小写,且使用首字母大写的形式分割单词;
6.引入其它类时,若要作为实例变量的在.h中引入。否则在.m中引入。
7.常量(#define, enums, const等)使用小写“k”作为前缀,首字母大写来分割单词。
如: #define kNOTIFICATION_UPLOAD_ALBUM @"kNotificationUploadAlbum"
8.书写静态字符串常量尽量使用static NSString * =@“”书写,增加运行效率;
如:
static NSString * sFirstNavControllerID = @"FirstNavSBID";
9. 指针“*”号的位置;
如:NSString *varName;
10.每行的长度: 每行尽量最多不超过120个字符;如果超过120个字符,则分开变量写或加\;
Google的80字符的标准有点少,这导致过于频繁的换行(Objectve-C的代码一般都很长,120字符是以官方api中比较长一点的为最大标准)
通过 “Xcode => Preferences => TextEditing => 勾选Show Page Guide / 输入120 => OK” 来设置提醒
例1:当一个变量放入方法中,导致整行代码非常长,就会看的比较累,可以采取分开来写;
不规范写法:
self.tabBar.selectionIndicatorImage = [UIImage zhuImageWithColor:AppSecondGlobalTintColor andSize:CGSizeMake(CGRectGetWidth(self.tabBar.bounds)/2, CGRectGetHeight(self.tabBar.bounds))];
规范写法:
CGSize size = CGSizeMake(CGRectGetWidth(self.tabBar.bounds)/2, CGRectGetHeight(self.tabBar.bounds));
self.tabBar.selectionIndicatorImage = [UIImage zhuImageWithColor:AppSecondGlobalTintColor andSize:size];
例2:字符串太长换行,加\;
NSString *str = [NSString stringWithFormat:@"http://mobileapi.3bee.com.cn/News/show_html?uid=132&"\
"app_key=5f341164f1717f2f381baab3563fc702&id=%@",self.aId];
11.方法的声明和定义
在 - 或 + 和返回值之间留1个空格。如果有多个参数,第二个参数方法描述应该和第一个参数保持一个空格间距;
如:
- (void)viewDidLoad;
+ (UIImage *)zhuImageWithColor:(UIColor *)color andSize:(CGSize)size;
当参数过长时,超过120行,则每个参数占用一行,以冒号对齐。
如:
/**
* @brief set appearance With all navigationBar's background and 标题
* @param
*/
- (void)zhNavigationBar_appearance_backgroundImageName:(NSString *)bagImgName
ShadowImageName:(NSString *)aShadowName
orBackgroundColor:(UIColor *)backgoundColor
titleColor:(UIColor *)aColor
titleFont:(UIFont *)aFont
* @brief set appearance With all navigationBar's background and 标题
* @param
*/
- (void)zhNavigationBar_appearance_backgroundImageName:(NSString *)bagImgName
ShadowImageName:(NSString *)aShadowName
orBackgroundColor:(UIColor *)backgoundColor
titleColor:(UIColor *)aColor
titleFont:(UIFont *)aFont
barItem_textFont:(UIFont *)textFont;
12.get方法和set方法
如项目使用runtime时候的一个属性代理:
- (void)setThirdDelegate:(id<ThirdLoginDelegate>)thirdDelegate
{
[self willChangeValueForKey:@"ThirdLoginController"];
objc_setAssociatedObject(self, &thirdLoginKey, thirdDelegate, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"ThirdLoginController"];
}
- (id<ThirdLoginDelegate>)thirdDelegate
{
return objc_getAssociatedObject(self, &thirdLoginKey);
{
[self willChangeValueForKey:@"ThirdLoginController"];
objc_setAssociatedObject(self, &thirdLoginKey, thirdDelegate, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"ThirdLoginController"];
}
- (id<ThirdLoginDelegate>)thirdDelegate
{
return objc_getAssociatedObject(self, &thirdLoginKey);
}
13.用C语言方式定义一个变量规范(.h声明,m文件实现):
.h文件用extern和const定义变量,在 @interface 接口之前定义;
const定义变量时,表明欲阻止这个变量被改变。
extern 表面这是个类的全局变量, 但是在定义时不能对其进行初始化。
如:
.h文件:
extern NSString *const kNotificationUserLoginIn;
@interface UserInfoUDManager : NSObject
.m文件
NSString *const kNotificationUserLoginIn = @"kNotificationUserLoginIn";
@implementation UserInfoUDManager
方法中就可以用来做变量名使用
[[NSNotificationCenter defaultCenter]postNotificationName:kNotificationUserLoginIn object:self];
14.@interface,@implementation2者与#import 之间都空一行,遇到定义变量的,也间隔一行为准。@synthesize紧接着@implementation写。
例如:
#import "UserInfoUDManager.h"
NSString *const kNotificationUserLoginIn = @"kNotificationUserLoginIn";
@implementation UserInfoUDManager
15.声明属性与实例变量
a:除了只使用于block的实例变量,声明实例变量一律以属性声明。
b:其它类要访问的实例变量和方法在.h接口文件中声明,否则声明于.m文件延展中作为私有变量。
c:属性及方法以功能块放在一起,实现一个功能的连续着放在一起,另一个功能的空一行开始声明。(建议,不是标准)
d:属性紧接@interface后开始,方法在属性后空一行开始。(建议,不是标准)
e:每个功能块可作简单注释说明,单个实例变量方法可不作注释,关键的或特殊的变量或方法单独作说明。注释放于变量声明上一行。
16.关键字在属性中的使用及技巧:
a:对象使用strong;
@property(nonatomic,strong) NSNumber *price;
b:字符串尽量使用copy;
@property(nonatomic,copy) NSString *albumTitle;
c:协议属性使用weak:
@property(nonatomic,weak) id<ImageEditViewPickerDelegate>delegate;
d:block使用copy:
@property (copy, nonatomic) ZXImageUploadProgressBlock progressBlock;
e:基本数据类型使用assign:
@property(nonatomic,assign) CGFloat upload_showProgress;
f:Bool值规范写法:
@property(assign,nonatomic,getter=isStarting) BOOL starting;
同时实现getter方法:
- (BOOL)isStarting
{
return _starting;
{
return _starting;
}
调用的时候用getter方法 if (!record.isStarting)
17.程序中变量、方法命名尽量能以字面意思表示功能,对于需要用注释来解释的部分代码,注释以如下格式表述:
/**
* 方法或变量说明
* @param 参数1说明(针对方法)
* @param 参数2说明(针对方法)
* @return 若方法有返回值则对返回值作说明
*/
推荐用VVDocumenter-Xcode插件作为注释插件,效果能增加美观,同时提高注释效率;
18.在block中:为了防止强引用,属性改为使用成员变量用“_”作为前缀
19.对于系统的常用类作实例变量声明时加入后缀:(蓝色标记为必须的)
UIViewController:VC UINavigationController :NVC
UIImage:Img UIImageView:ImgView UIView:View UILabel:Lab
UIButton:Btn NSMutableArray:MArray NSDictionary:Dict NSMutableDictionary:Dict UITextField:TextField UITextView:TextView
UINavigationBar:NBar UIToolBar:TBar UISearchBar:SBar
UITextField:TextField UITextView:TextView
NSArray:Array
NSMutableDictionary:MDict
NSString:Str NSMutableString:MStr
NSSet:Set NSMutableSet:MSet
20. 对nil的检查,仅在有业务逻辑需求时检查nil,而非为了防止崩溃;
21.BOOL陷阱:
a. 将int值转换为BOOL时应特别小心。避免直接和YES比较;
b.Objective-C中,BOOL被定义为unsigned char,这意味着除了YES(1)和NO(0)外,它还可以是其他值。禁止将int直接转换为BOOL。
c.NSNumber 能和BOOL值互相转换;
如:
@property (readonly) BOOL boolValue;
- (NSNumber *)initWithBool:(BOOL)value NS_DESIGNATED_INITIALIZER;
d.在oc中,只允许使用BOOL,不要使用c的写法bool;
e.禁止直接将BOOL值和YES/NO比较:
错误:
BOOL great = [foo isGreat];
if (great == YES)
正确:
BOOL great = [foo isGreat];
if (great)...
22.使用respondsToSelector:/instancesRespondToSelector:去检测对象是否支持某个方法或最新方法,防止奔溃。
1. respondsToSelector既可以检查类(是否响应指定类方法),也可以检查实例(是否响应指定实例方法)
既可以类方法-类调用,类方法,也可以实例方法检查-实例调用,实例方法。
2. instancesRespondToSelector只能写在类名后面,但检测的是实例(是否响应指定实例方法)
可以认为[Test instancesRespondToSelector:sel]等价于[obj respondsToSelector:sel];
例如:
if ([UITableView instancesRespondToSelector:@selector(setSeparatorInset:)])
{
[[UITableView appearance]setSeparatorInset:UIEdgeInsetsZero] ;
}
{
[[UITableView appearance]setSeparatorInset:UIEdgeInsetsZero] ;
}
注:
每次iOS升级,都会有一批方法被弃用,如果我们使用了弃用的方法,在编译时,xcode会默认提示warning。虽然这些被弃用的方法一般不会马上从SDK中消失,但是为了提高以后新版本的兼容性,还是建议在编写的时候进行一定的处理。一般使用respondsToSelector:去检测对象是否支持最新的方法。
23.使用flashScrollIndicators实例方法,在继承于UIScrollView的tableView/UICollectionView等闪一下滚动条,暗示是否有可滚动的内容。可以在ViewDidAppear或[table reload]之后调用,增加用户体验度。
24.遇到独立的某个功能,多个模块会复用的,或者程序比较多的,需要抽取出来,自定义类;用代理协议的方式封装;
比如
a.简单的从 UIImagePickerController获取照片-可以考虑建立一个UIViewController的类目,把所有判断和UIActionSheet合成封装一起;
b.无限循环无缝链接轮播图-可以考虑自定义UIView的子类,把NSTimer和UIScrollView合成封装一起;
c.输入框UITextField填写日期时-可以考虑封装一个继承UITextField的日期选择器;
所有自定义封装,采取约束的方式布局,如果是别的项目也通用的功能,则用原生约束方式写;同时所有封装需要初始化返回
- (id)initWithCoder:(NSCoder *)aDecoder
25.一些常用模块采用基类的方式执行:
a.网络请求,用AFNetworking框架,写一个BaseHttpAPI基类,把一些错误信息都在那里执行block回调;
b.Model层,用JSONModel或Mantle,写一个BaseModel基类;
c.web页面,也可以写个WebViewController基类;
d.横竖屏旋转,写个UINavigationController的基类,处理所有旋转和非旋转的问题;
26.第三方开源管理器,使用Cocoapods管理;防止冲突,项目组由一人导入;
27.所有第三方开源,原则上,只提倡使用Github排名前50的,而且不用已经停止更新的,如ASIHttpRequest,导入cocoapods,公用学习;
其余功能UI效果开源,只提倡自己用技术仿写,或者单独放入工程Library第三方管理中;
28.整个项目不允许使用硬编码,0,1,2数字表示的; 除了个别tableView简单的地方;一律改用定义变量;
29.定义枚举一律使用typedef NS_ENUM方式:
例如:
typedef NS_ENUM(NSInteger,ThirdLoginType){
thirdLoginTypeWeiXin=1,
thirdLoginTypeQQ=2,
thirdLoginTypeSina=3
thirdLoginTypeWeiXin=1,
thirdLoginTypeQQ=2,
thirdLoginTypeSina=3
};
30.自己写的类方法或实例方法,返回值,一律用 instancetype,不用id;
例如:
+(instancetype)getInstance;
31.所有注册的通知,必须在dealloc中释放,以免崩溃;
例如:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter]removeObserver:self];
{
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
二.文件结构
使用MVC设计理念:
1.github排名较高的,第三方平台集成的,用cocoapods管理;
2.图片资源文件,全部导入Assets.xcassets文件。采用目录结构划分图片资源;
3.建立Resources文件,用于放入storyboard,xib,gif,plist,Image大图,音频,视频等文件,若多个功能块共用的,建立Common文件夹,放入其中;
4.建立Controller文件,按业务划分分组,或按模块划分分组;以功能名字命名;所有Controller需要带Controllr后缀;
为了更加适合管理和开发,把相对应的子View放入对应Controller目录下;
5.Model文件,以实用Mantle为例,以业务/模块划分一个类,把相对应的业务model都写入一个Model里,比如建立UserModel用户model,PageModel-分页model,OderModel-订单model等;
例如以用户Model为例,建立UserModel,把所有登录,注册,忘记密码等model都写入UserModel内;
6.建立DataBase文件,主要写管理数据持久化的业务,把由CoreData,FMDB,NSUserDefault,NSFileManager属性列表,编码解码等带来的自定义管理文件类放入这里;
比如用户本地数据类:
可以建立NSUserDefaultData,以类方法的形式,创建登录,退出,是否登录,设置用户数据,更改本地用户的某个数据,获取数据等;
建议使用TMCache开源建立本地用户管理数据;
7.建立Library文件,把所有除了cocoa pod管理外的开源,第三方平台集成,自己封装的类,放入此处;
8.建立Marco宏文件,同时建立
(1)AppMarco.h ,用于写app的常用宏,比如,主题色,主题文字色,背景色,占位图,token失效,请求成功等;
(2)HTTPMarco.h,用于写Http网络有关的变量,url地址,baseURL,appID,第三方平台的key等;以 #pragma mark-URL分割,例如:
static NSString* kHTTP_USERID_KEY= @"userid";
(3)NotificationMarco.h, 定义一些通知的变量;
(4)StoryboardHeader.h子类,定义storyboard的storyboard名字,storyboard Segue,storyboard ID 3种不同变量;
9.建立Helper文件,主要写所有类的网络数据业务功能,建立AppAPIHelper类;
10.建立General文件,用于放二级业务代码;
建立BaseClass子文件夹(管理各种基类,如BaseHttpAPI数据请求基类,Model基类,webViewController基类等),HttpAPI文件夹(以model分类建立方式,建立网络请求),
Protocol文件夹,Category文件夹;