[toc]
一般性原则
避免命名歧义
Not Preferred
`displayName` /// Does it display a name or return the receiver’s title in the user interface?
一致性
整个工程的命名风格要保持一致性,最好和Apple.Inc SDK的代码保持统一。
Not Preferred
@interface MNMMovieCommentList : MTBModel
@property (nonatomic, strong) NSMutableArray *commentsList;
@property (nonatomic, strong) NSNumber *totalCount; //影评总条数(过滤后),用于分页计算
- (NSUInteger)dataCount;
@end
常量、变量、数据类型
基本类型
- 结合数值和币种的money类
- 由一个起始值和一个结束值组成的range类
Not Preferred
@property (nonatomic, assign) CGFloat totalRate;
@property (nonatomic, assign) CGFloat musicRate;
@property (nonatomic, assign) CGFloat pictureRate;
@property (nonatomic, assign) CGFloat directorRate;
@property (nonatomic, assign) CGFloat storyRate;
@property (nonatomic, assign) CGFloat playRate;
@property (nonatomic, assign) CGFloat impressionRate;
NSDictionary *infoDic = @{
@"video" : @{
@"count" : self.movieDetailData.video.count ? : @(0),
@"url" : self.movieDetailData.video.img ? : @""
},
@"photo" : @{
@"count" : self.movieDetailData.stageImg.count ? : @(0),
@"url" : ((MovieImageEntity *) [self.movieDetailData.stageImg.list mt_objectAtIndex:0]).imageUrl ? : @""
}
};
常量
- 常量应该用static声明为静态常量,而不要用#define。实际上,如果一个变量既声明为static,又声明为const,那么编译器根本不会创建符号,而是会像#define预处理指令一样做替换。
- 若常量局限于某编译单元,也就是实现文件之内,则在前面加字母k;若常量在类之外可见,则通常以类名为前缀。
Preferred
static const NSTimeInterval ZOCSignInViewControllerFadeOutAnimationDuration = 0.4
Not Preferred
- (MNEMemberProgress)p_memberProgress:(CGFloat)floatEmpirical
{
floatEmpirical = floatEmpirical > 0 ? floatEmpirical : 0;
if (floatEmpirical >= 8000)
{
return MNEMemberProgress_Diamond;
}
else if (floatEmpirical >= 3000)
{
return MNEMemberProgress_Platinum;
}
else if (floatEmpirical >= 1000)
{
return MNEMemberProgress_Gloden;
}
else if (floatEmpirical >= 200)
{
return MNEMemberProgress_Silver;
}
else
{
return MNEMemberProgress_Normol;
}
}
布尔值
- Objective-C使用YES和NO。
- true和false在CoreFoundation、C或C++代码使用。
- BOOL在Objective-C 中被定义为signed char,能被设置为8位。但YES被定义为1。
Not Preferred
self.baseTableView.showsHorizontalScrollIndicator = FALSE;
if (self.arraySectionBtns == nil)
{
self.arraySectionBtns = [NSMutableArray array];
}
if (self.isSubscribe == YES)
{
return;
}
语法糖
应该使用可读性更好的语法糖来构造NSArray,NSDictionary等数据结构,避免使用冗长的alloc,init方法。
运算符、表达式
三目运算符
一个条件语句的所有的变量应该是已经被求值了的。否则计算多个条件子句通常会让语句更加难以理解。
当三元运算符的第二个参数返回和条件语句中已经检查的对象一样的对象的时候,可以省略。
Preferred
result = object ? : [self createObject];
if嵌套
不要嵌套 if 语句。使用多个 return 可以避免增加循环的复杂度,并提高代码的可读性。因为方法的重要部分没有嵌套在分支里面,可以很清楚地找到相关的代码。
Not Preferred
- (BOOL)handleSchemeOpenURL:(NSURL *)aUrl
{
NSString *stringScheme = aUrl.scheme;
NSString *stringHost = aUrl.host;
/// 检查Scheme和Host是否合法
if ([stringScheme mt_compareCaseInsensitive:mnkScheme_mtime])
{
if ([stringHost mt_compareCaseInsensitive:mnkScheme_host])
{
return [self handleAppLinkUrl:aUrl animation:NO];
}
}
return NO;
}
括号指定计算优先级
Not Preferred
if(*p++)
复杂的条件表达式
- 当有一个复杂的if子句的时候,应该把它们提取出来赋给一个 BOOL变量,这样可以让逻辑更清楚,而且让每个子句的意义体现出来。
- 以查询取代临时变量更适合,毕竟临时变量只在它所处的那个函数中才有意义,局限性较大,而函数则可以在对象的整个生命中都有用,并且可被其他对象使用。
Not Preferred
if (!badgeValue || [badgeValue isEqualToString:@""] || ([badgeValue isEqualToString:@"0"] && self.shouldHideBadgeAtZero))
{
// [self removeBadge];
self.badge.hidden = YES;
}
控制语句
Switch
可以运用面向对象的多态概念来避免Switch语句。
Preferred
[MNCTempletTableViewCell templetTableViewCellWithTableView:tableView indexPath:indexPath Type:model.templetType BeUsedIn:MNETempletBeUsedIn_IndexSelected]
[(MNCTempletTableViewCell *) cell mt_setObject:model]
函数
过长的函数
每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。每个函数的粒度都很小,那么函数被复用的机会就更大,函数的覆写也会更容易。
粒度小的函数使得高层函数读起来就像一系列注释。
过长的参数列
使用参数对象解决问题。
Preferred
- (void)updateContent:(MNMTempletBottomView *)modleTempletBottomView
私有方法
私有方法使用前缀表明。
Preferred
- (void)p_privateFunction{}
将查询函数和修改函数分离
某个函数既返回对象状态值,又修改对象状态。则应该将查询函数和修改函数分离。任何有返回值的函数,都不应该有看得到的副作用。
重复代码
- 同一个类的两个函数含有相同的表达式
- 两个互为兄弟的子类内含相同表达式
- 毫不相干的类出现相同的表达式
封装向下转型
某个函数返回的对象,需要由函数调用者执行向下转型。这种情况下你不应该要求用户承担向下转型的责任,应该尽量为他们提供准确的类型。
Not Preferred
- (NSNumber *)obtainPayEndTime
{
return _modelOrderDetail.payEndTime;
}
self.requestReSendSMS.stringSubOrderID = [[self obtainSubOrderId] stringValue];
类
过大的类
Objective-C
枚举类型
使用NS_ENUM,以获得强类型检查。
Not Preferred
typedef enum : NSUInteger {
/// 等级礼包
MNE_Member_PopType_Level = 1,
/// 生日礼包
MNE_Member_PopType_Birthday = 2
} MNE_Member_PopType;
Preferred
typedef NS_OPTIONS(NSUInteger, NSWindowMask) {
NSBorderlessWindowMask = 0,
NSTitledWindowMask = 1 << 0,
NSClosableWindowMask = 1 << 1,
NSMiniaturizableWindowMask = 1 << 2,
NSResizableWindowMask = 1 << 3
};
属性
- 描述BOOL属性的词如果是形容词,那么setter不应该带is前缀,但它对应的getter访问器应该带上这个前缀。
- 应该总是使用点语法来访问和修改属性。
Preferred
@property(nonatomic,getter=isOn) BOOL on;
Not Preferred
if ([objet isKindOfClass:[NSArray class]])
{
return [(NSArray *) objet count];
}
/// 表示只有一组
return self.arrayDataSourceItems.count;
new()
- 不要尝试调用NSObject类的
new()
方法,也不要在它的子类中复写这个方法。 - alloc负责创建对象,这个过程包括分配足够的内存来保存对象,写入isa指针,初始化引用计数,以及重置所有实例变量。
- init负责初始化对象,这意味着使对象处于可用状态。这通常意味着为对象的实例变量赋予合理有用的值。
instancetype
使用instancetype确保编译器正确地推断结果类型。
Preferred
+ (instancetype)personWithName:(NSString *)name;
Lazy Loading
Preferred
- (NSDateFormatter *)dateFormatter {
if (!_dateFormatter) {
_dateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[_dateFormatter setLocale:enUSPOSIXLocale];
[_dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"];
}
return _dateFormatter;
}
错误处理
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
当一个方法通过引用返回一个错误参数,应该检测返回值的状态,而不是错误参数的状态。
Preferred
NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL
options:0
error:&anyError];
if (!success) {
NSLog(@"Write failed with error: %@", anyError);
// present error to user
}
Not Preferred
NSError *error = nil;
BOOL isSucess = [[NSFileManager defaultManager] removeItemAtPath:self.stringFilePathCaches
error:&error];
if (error)
{
if (mtkString_Is_Valid(error.localizedDescription))
{
// 如果解析失败,上传解析失败原因
NSString *stringErrorLog = [NSString stringWithFormat:@"清除“MIT_Caches”文件 失败,\n原因:%@",
error.localizedDescription];
stringErrorLog = stringErrorLog;
LOG_Foundation_Info_(@"%@", stringErrorLog);
}
}
return isSucess;
ARC下函数命名
ARC通过命名约定将内存管理规则标准化。若方法名以下列词语开头,则其返回的对象归调用者所有。若方法名不以上述四个词语开头,则标示其返回的对象并不归调用者所有,在这种情况下,返回的对象会自动释放。
- alloc
- new
- copy
- mutableCopy
Not Preferred
- (void)newShareContent:(ShareViewContent *)aModelContent
statisticsParams:(NSDictionary *)aDicStatistics;
CGRect
Your application can standardize a rectangle—that is, ensure that the height and width are stored as positive values—by calling the CGRectStandardize function. All functions described in this reference that take
CGRect
data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the
CGRect
data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
CGGeometry
Preferred
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
NS_DESIGNATED_INITIALIZER
- 如果创建类实例的方式不止一种,那么这个类就会有多个初始化方法,不过仍然要选定其中一个作为全能初始化方法,令其他初始化方法都来调用它。这样当底层数据存储机制改变时,只需修改此方法的代码就好,无须改动其他初始化方法。如果子类的全能初始化方法与超类方法的名称不同,那么总应覆写超类的全能初始化方法,必
要时甚至在其中抛出异常。 - 每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上。
- 使用
NS_DESIGNATED_INITIALIZER
表明全能初始化方法。
isEqual:
- 当实现相等性的时候记住这个约定:需要同时实现isEqual和 hash方法。如果两个对象是被isEqual认为相等的,它们的 hash方法需要返回一样的值。但是如果hash返回一样的值,并不能确保他们相等。
- 一定要注意hash方法不能返回一个常量。这是一个典型的错误并且会导致严重的问题,因为实际上hash方法的返回值会作为对象在hash散列表中的key,这会导致hash表100%的碰撞。
Not Preferred
- (NSUInteger)hash
{
return 1;
}
Notification
[ 触发通知的类名 ] + [Did | Will] + [ 动作 ] + Notification
Preferred
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
Clang
忽略没用使用变量的编译警告
Preferred
- (NSInteger)giveMeFive
{
NSString *foo;
#pragma unused (foo)
return 5;
}
标注、警告、错误
Preferred
/// MARK:a
/// TODO:b
/// FIXME:c
/// !!!: d
/// ???:e
#error Whoa, buddy, you need to check for zero here!
#warning Dude, don't compare floating point numbers like this!
引入断言
注释
self-documenting
其他
封装集合
我们常常会在一个类中使用集合来保存一组实例。集合的处理方式应该和其他种类的数据不同。取值函数不该返回集合自身,因为这会让用得以修改集合内容而集合拥有者确一无所悉。
Not Preferred
@interface MNMMovieCommentList : MTBModel
@property (nonatomic, strong) NSMutableArray *commentsList;
- (void)replaceByNewMovieCommentListEntity:(MNMMovieCommentList *)newMovieCommentListEntity;
- (void)addObjectsFromNewMovieCommentListEntity:(MNMMovieCommentList *)newMovieCommentListEntity;
@end
Preferred
- (void)addElement:(elementType)anObj;
- (void)removeElement:(elementType)anObj;
- (NSArray *)elements;
缩略词
缩略词
参考资料
- https://baike.baidu.com/item/重构:改善既有代码的设计/9277420
- https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146-SW1
- https://github.com/github/objective-c-style-guide
- https://github.com/raywenderlich/objective-c-style-guide
- https://github.com/NYTimes/objective-c-style-guide
- http://www.jianshu.com/p/25938c097e25