1. nil检查
- 用
if(!boolValue)
代替if(boolValue == nil)
例子:AMKElement判断非主线程
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
return;
}
2. 将次要分支写在前,并用return,防止一大坨代码包在if里面
推荐:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
不推荐:
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}
例子:
// 若不在主线程,则切到主线程再调用本方法
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:@selector(doubleTap) withObject:nil waitUntilDone:YES];
return;
}
// 在主线程
// 处理一大坨事情
3. 复杂的if语句,应该把判断条件提取出来设为一个BOOL变量
例子:
BOOL isMathced = [predicate evaluateWithObject:element];
if (isMathced) {
UIView *view = (UIView *)element;
}
return isMathced;
4. 三元运算符
例子:
UILabel *label = (labels.count > 0 ? labels[0] : nil);
- 当三元运算符的第一个分支就是判断条件时,建议写成:
result = object ? : [self createObject];
例子:
UIWindow *window = _viewController.view.window ?: [[[UIApplication sharedApplication] delegate] window];
5.错误处理
推荐:
NSError *error = nil;
if (![self trySomethingWithError:&error]) {
// Handle Error
}
不推荐:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// Handle Error
}
错误例子:
- <s>如上删除文件,在删除的过程中可能对error变量进行赋值,然后打印error,可知错误信息</s>
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
if (error) {
NSLog(@"Move failed: %@", error);
}
- 好吧我一开始理解错了,这个例子给的是NSError常规的用法,但是此条规则的本意是不推荐检查error的引用,而应该检查方法的返回值
正确例子:
NSError *error = nil;
if (![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
NSLog(@"Move failed: %@", error);
}
6.常量
- 推荐使用驼峰写法
- 建议用static声明为静态常量,不建议用define定义常量,除非你要定义宏
例子:
static const double kElementSufficientlyVisiblePercentage = 0.75;
- 需要暴露给外部的常量,在头文件中用extern标示,并在实现文件中赋值
例子:
// xxx.h
extern NSString *const AMKJavaScriptWillStartLoadingNotification;
// xxx.m
NSString *const AMKJavaScriptWillStartLoadingNotification = @"AMKJavaScriptWillStartLoadingNotification";
7.方法名
- 参数前加一个描述性的关键词
- 不建议用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.
8.字面值
- 建议用字面值创建不可变的NSString, NSDictionary, NSArray, 和 NSNumber 对象,不要将 nil 传进 NSArray 和 NSDictionary 里
推荐:
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];
- 对于可变版本,推荐使用 NSMutableArray, NSMutableString 等
-
@[] mutableCopy
这种写法不推荐 - 但是还是使用可变拷贝
mutableCopy
推荐:
NSMutableArray *tmpList = [[NSMutableArray alloc] init];
NSMutableString *mutableString = [originalString mutableCopy];
不推荐:
NSMutableArray *tmpList = [@[] mutableCopy];
9.初始化方法
- 指定初始化方法(designated initializer)
- 标注哪一个初始化方法是designated,用编译器指令
__attribute__((objc_designated_initializer))
这样如果新的designated initializer没有调用超类的designated initializer,就会警告
例子:
#define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
- (instancetype)init;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
10.instancetype V.S. id
这篇文章对两者的差别讲的蛮清楚的
http://blog.csdn.net/wzzvictory/article/details/16994913
- 原来未知的返回类型对象,都 id 关键词;直到clang 3.5开始提供 instancetype 关键词
- 关联返回类型:会返回一个方法所在类类型的对象,系统的alloc、new、init等是关联返回类型的,比如
[NSArray alloc]
返回的就是NSArray*
,[[NSArray alloc] init]
返回的也是NSArray*
- instancetype的作用,就是使那些非关联返回类型的方法返回所在类的类型
// 申明返回类型为id
@interface NSArray
+ (id)constructAnArray;
@end
[NSArray constructAnArray]; // 得到的返回类型和申明的一样,即id
// 申明返回类型为instancetype
@interface NSArray
+ (instancetype)constructAnArray;
@end
[NSArray constructAnArray]; // 得到的返回类型是所在类的类型,即NSArray*
- 返回类类型的好处是,编译器能够在编译截断就判断返回类型能否实现
// 编译时会报错 : "No visible @interface for `NSArray` declares the selector `mediaPlaybackAllowsAirPlay`"
[[[NSArray alloc] init] mediaPlaybackAllowsAirPlay];
// 编译时不会报错
[[NSArray array] mediaPlaybackAllowsAirPlay];
- instancetype 的优势是能返回所在类类型(id 只能返回未知类型)
- id 的优势是既能当返回值,又能当参数(instancetype 只能当返回值)
- 所以最好的做法就是,用 id 当参数,用 instancetype 当返回值(当然针对的是会返回类的实例的方法)
- 这样做法一个额外的好处就是,可以一目了然知道哪些方法返回了类的实例
例子
- (instancetype)initWithAccessibilityElement:(id)accessibilityElement{
self = [super init];
if (self) {
_accessibilityElement = accessibilityElement;
}
return self;
}
写到这里,例子很多是从@巴格的InAppMonkey中摘录出来的,不得不说,代码功底很好,几乎所有写法都是Best Practice,值得学习!!!
11. 懒加载
- 当实例化一个对象耗费资源较多,就需要重写getter方法以延迟实例化,而不是在init方法里给对象分配内存
例子
- (NSString*)automationDirectory{
if (!_automationDirectory) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
_automationDirectory = [paths objectAtIndex:0];
_automationDirectory = [_automationDirectory stringByAppendingPathComponent:@"TMUIAutomation"];
}
if (![[NSFileManager defaultManager] fileExistsAtPath:_automationDirectory]) {
[[NSFileManager defaultManager] createDirectoryAtPath:_automationDirectory withIntermediateDirectories:NO attributes:nil error:nil];
}
return _automationDirectory;
}
12. 参数断言
- 你的方法可能要求一些参数来满足特定的条件(比如不能为nil),在这种情况下最好使用
NSParameterAssert()
来断言条件是否成立或是抛出一个异常
例子
- (NSString *)grey_printDescriptionForElement:(id)element atLevel:(NSUInteger)level {
AMKSureNotStopReturnValue(nil)
NSParameterAssert(element);
NSMutableString *printOutput = [NSMutableString stringWithString:@""];
//...
}
13. Category
- 建议在category类名中使用前缀
例子
@interface NSString(AMK)
- (NSString *)amk_decodeHTMLCharacterEntities;
- (NSString *)amk_encodeHTMLCharacterEntities;
@end
14. Protocol
- 这块 Zen 给的建议没有看明白
15. NSNotification
- 当自定义
NSNotification
时,应该把通知的名字定义为一个字符串常量,然后在公开接口中将其声明为extern
例子
// AMKBridge.h
extern NSString *const AMKReloadNotification;
// AMKBridge.m
NSString *const AMKReloadNotification = @"AMKReloadNotification";
16. pragma
- 当你使用ARC的时候,编译器帮你插入了内存管理相关的调用。但是这样可能产生一些烦人的事情。比如你使用
NSSelectorFromString
来动态地产生一个 selector 调用的时候,ARC不知道这个方法是哪个并且不知道应该用那种内存管理方法,你会被提示performSelector may cause a leak because its selector is unknown
- 如果你知道你的代码不会导致内存泄露,你可以通过加入这些代码忽略这些警告
例子
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[navBar.topItem.leftBarButtonItem.target performSelector:navBar.topItem.leftBarButtonItem.action withObject:navBar.topItem.leftBarButtonItem];
#pragma clang diagnostic pop
网友评论