文章目录
没有规矩不成方圆。说说我比较推崇的代码规范。求同存异,别的规范也是可取的,具体问题具体分析。
工程结构
-
为整个工程创建 workspace
-
合理的工程目录结构。
-
Core:通用的机制实现类:统一的任务管理,模块管理,服务管理。
-
General:公用类和方法,包括工程内基类(Base),公用Category,公用UI组件(CustomUI),公用辅助方法(Helper)和宏定义(Macro)。
-
Vendors:第三方库。不建议将三方库直接添加进工程,建议使用cocoapods管理。
-
先按业务划分,再按照MV-X来划分。方便解耦,模块化。
将TableViewCell放在View目录,把具体的ViewController放在Controller目录,图片等资源放到Resource目录,工具类放到Utils目录。
-
头文件
#import的顺序按照:系统类、第三方类、自己类 ( 或者按照字母排序)
#import <系统库>
#import <系统库>
#import <第三方库>
#import <第三方库>
#import “其他类”
常量
- 常量用static,不使用#define
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
- 命名
若常量局限于某“编译单元”(translation unit,也就是“实现文件”,implementation file)之内,则在前面加字母k;
若常量在类之外可见,则通常以类名为前缀。
static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";
NSString * const kTTTStrikeOutAttributeName = @"TTTStrikeOutAttribute";
-
声明Cell的重用
字符k+cell的名称+identifier
static NSString *const kQuestionCellIdentifier = @"kQuestionCellIdentifier";
- const声明字符串提供外部使用
h声明m实现且让其他的类用使用。如果导入是UIKit类就使用UIKIT_EXTERN,如果是Founction使用关键词FOUNDATION_EXTERN。
//h声明
FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;
//m实现
NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";
- Notifications
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
例如:
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
- Exceptions
[Prefix] + [UniquePartOfName] + Exception
例如:
NSColorListIOException
NSColorListNotEditableException
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException
对于#define宏命名
单词全部的大写,单词之间用_分割。
#define RCT_EXPORT_SHADOW_PROPERTY(name, type) \
+ (NSArray<NSString *> *)propConfigShadow_##name { return @[@#type]; }
枚举类型
使用NS_ENUM、NS_OPTIONS语法,不使用enum语法(除非c语言),新的语法有更强的类型检查和代码补全。
typedef NS_ENUM(NSInteger, NetworkStatus) {
// Apple NetworkStatus Compatible Names.
NotReachable = 0,
ReachableViaWiFi = 2,
ReachableViaWWAN = 1
};
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
SDWebImageDownloaderLowPriority = 1 << 0,
SDWebImageDownloaderProgressiveDownload = 1 << 1,
SDWebImageDownloaderUseNSURLCache = 1 << 2,
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
}
对于NS_OPTIONS类型多值用|连接,不能用+。
NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay
代码组织
使用#pragma mark - 进行分组
#pragma mark - Lifecycle
- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
#pragma mark - Public
- (void)publicMethod {}
#pragma mark - rewrite
- (void)fatherMethod {}
#pragma mark - Private
- (void)privateMethod {}
#pragma mark - Draw
- (void)drawRect:(CGRect)rect {}
#pragma mark - Events
- (IBAction)submitData:(id)sender {}
- (void)onBtnClicked:(id)sender {}
#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate
#pragma mark - Properties
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {}
#pragma mark - NSObject
- (NSString *)description {}
注释代码
应该尽量做到清晰可读,做到自解释。当需要注释时,注释应该更多地用来描述,为什么这样做。注释保持更新,历史的注释应该删除。
-
可能存在问题的代码应该加上// FIXME:的注释,并详细描述(注意用半角的冒号)。
-
需要添加逻辑的代码应该加上// TODO:,并详细描述。
-
不是源码文件创建者修改了代码,应该在修改的代码上加上// Modified:,并写明修改人、修改时间、修改原因等信息。
命名
- 必须尽量遵守苹果官方的命名规范,应该使用长的、描述清楚的函数名、变量名。应该使用三个字母的前缀来给类、常量进行命名。
- 控件命名中UILabel结尾加上Label,UIImageView结尾记上ImageView等等。
@property(nonatomic,strong) UILabel *userNameLabel;
函数(Method)
在函数签名中(函数声明或定义),在函数类型(-/+符号)后,必须有一个空格。参数之间也要有一个空格。参数的keyword不要为空。除了第一个参数,后面的参数的keyword尽量不要加with、and等连词的前缀,keyword更不应该直接用with、and。
//应该
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
//不应该
-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height; // Never do this.
一般情况下,不要缩写名字,即使它们很长。少数一部分缩写是非常通用的,也有很长的使用历史。可用缩写
Abbreviation | Meaning and comments |
---|---|
alloc | Allocate. |
app | Application. For example, NSApp the global application object. However, “application” is spelled out in delegate methods, notifications, and so on… |
calc | Calculate… |
dealloc | Deallocate. |
func | Function. |
horiz | Horizontal. |
info | Information. |
init | Initialize (for methods that initialize new objects). |
int | Integer (in the context of a C int—for an NSInteger value, use integer). |
max | Maximum. |
min | Minimum. |
msg | Message. |
nib | Interface Builder archive. |
pboard | Pasteboard (but only in constants). |
rect | Rectangle. |
Rep | Representation (used in class name such as NSBitmapImageRep)… |
temp | Temporary. |
vert | Vertical. |
你也可以使用下面这些在计算机行业公认的专业术语:
ASCII PDF XML HTML URL RTF HTTP TIFF JPG PNG GIF LZW ROM RGB CMYK MIDI FTP
初始化方法
返回值应该使用instancetype来代替之前的id。
- (instancetype)init {
self = [super init];
if (self) {
// ...
}
return self;
}
单例模式
单例模式应该使用dispatch_once来保证创建时的线程安全。
static id sharedInstance = nil;
+ (instancetype)sharedInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
//重写+ allocWithZone:此方法在调用alloc时必会调用.use object initializers instead
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
//对于单例,应该无论以何种方式创建都应该只有一份内存的, 对于ARC的创建的方法还有copy,mutableCopy,但必须遵守协议<NSCopying,NSMutableCopying> , 执行copy,MutableCopy的时候必定会执行copyWithZone,mutableCopyWithZone
- (instancetype)copyWithZone:(NSZone *)zone
{
return sharedInstance;
}
- (instancetype)mutableCopyWithZone:(NSZone *)zone
{
return sharedInstance;
}
//暂不考虑MRC
Error处理
当函数返回NSError的引用,调用时要根据返回值进行判断,而不是NSError的值,因为苹果的一些api会往NSError中写一些垃圾数据,如果根据NSError进行判断可能会有问题。
ARC中严重不推荐使用try catch,可能内存泄露。
NSError *error;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
黄金路径(GoldenPath)
当if判断有多层嵌套,应该尽量使用黄金路径,即不符合条件的分支先返回,这样可以避免分支嵌套。
- (void)someMethod {
if (!condition1) {
return;
}
//Do something1
if (!condition2) {
return;
}
// Do something 2
}
尤达表达式
不要使用尤达表达式。
if ([myValue isEqual:@42]) {}
三元运算符
三元运算符, ?: , 应该只在它能提高代码整洁性而且逻辑清晰的情况下使用。判断条件应该只有一个,多于一个的判断条件应该用if或临时变量来写。
#CGRect相关方法
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);
布尔值
- 使用YES和NO来表示布尔值。
- true和false除了CoreFoundation和C、C++代码中,不应该被使用。
- nil被判断为NO,没有必要与nil进行比较。
- BOOL值可以最大可以达到8位,而YES只是被设置为1,尽量不要跟YES比较。
//应该
if (someObject) {}
if (![anotherObject boolValue]) {}
//不应该
if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.
- 如果布尔类型的属性名是一个形容词,那么属性虽然可以忽略is 前缀,但最好仍提供带is前缀的getter方法。
@property (assign, getter=isEditable) BOOL editable;
使用NSInteger
Apple的UIKit等代码一般都是用的NSInteger。NSInteger在32位系统是 int,64位系统是long 。系统的代码用的是 NSInteger 的话,你使用int的话,可能不够大而造成崩溃。
分类
没有命名空间的概念。在分类中的所有方法都加上特定前缀,前缀需小写。如
NSObject+XSTest.h
@interface NSObject (XSTest)
- (BOOL)xs_isEmptyObject;
@end
这里为分类名以及分类中的方法加上XS的前缀。
#摘录
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html
http://www.aichengxu.com/ios/7566968.htm
http://reviewcode.cn/article.html?reviewId=9
http://www.cnblogs.com/netfocus/p/3896118.html
http://www.cocoachina.com/ios/20170105/18515.html
https://github.com/objc-zen/objc-zen-book
https://juejin.im/entry/583a9eafa22b9d006c0eae3c
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CodingGuidelines/Articles/APIAbbreviations.html
https://www.jianshu.com/p/06cc78e8b8b6