iOS中书写代码规范34条小建议
2016-03-07 15:45
393 查看
1.精简代码, 返回最后一句的值,这个方法有一个优点,所有的变量都在代码块中,也就是只在代码块的区域中有效,这意味着可以减少对其他作用域的命名污染。但缺点是可读性比较差
NSURL *url = ({ NSString *urlString = [NSString stringWithFormat:@"%@/%@", baseURLString, endpoint]; [NSURL URLWithString:urlString]; });
2.关于编译器:关闭警告:
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [myObj performSelector:mySelector withObject:name]; #pragma clang diagnostic pop
3.忽略没用的变量
#pragma unused (foo) 明确定义错误和警告 #error Whoa, buddy, you need to check for zero here! #warning Dude, don't compare floating point numbers like this!
4.避免循环引用
如果【block内部】使用【外部声明的强引用】访问【对象A】, 那么【block内部】会自动产生一个【强引用】指向【对象A】
如果【block内部】使用【外部声明的弱引用】访问【对象A】, 那么【block内部】会自动产生一个【弱引用】指向【对象A】
__weak typeof(self) weakSelf = self; dispatch_block_t block = ^{ [weakSelf doSomething]; // weakSelf != nil // preemption, weakSelf turned nil [weakSelf doSomethingElse]; // weakSelf == nil }; 最好这样调用: __weak typeof(self) weakSelf = self; myObj.myBlock = ^{ __strong typeof(self) strongSelf = weakSelf; if (strongSelf) { [strongSelf doSomething]; // strongSelf != nil // preemption, strongSelf still not nil(抢占的时候,strongSelf 还是非 nil 的) [strongSelf doSomethingElse]; // strongSelf != nil } else { // Probably nothing... return; } };
5.宏要写成大写,至少要有大写,全部小写有时候书写不提示参数;
6.建议书写枚举模仿苹果——在列出枚举内容的同时绑定了枚举数据类型NSUInteger,这样带来的好处是增强的类型检查和更好的代码可读性,示例:
// 不推荐写法 typedef enum{ UIControlStateNormal = 0, UIControlStateHighlighted = 1 << 0, UIControlStateDisabled = 1 << 1, } UIControlState;
// 推荐写法 typedef NS_OPTIONS(NSUInteger, UIControlState) { UIControlStateNormal = 0, UIControlStateHighlighted = 1 << 0, UIControlStateDisabled = 1 << 1, };
7.建议加载xib,xib名称用NSStringFromClass(),避免书写错误
// 推荐写法 [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([DXRecommendTagVCell class]) bundle:nil] forCellReuseIdentifier:ID];
// 不推荐写法 [self.tableView registerNib:[UINib nibWithNibName:@"DXRecommendTagVCell" bundle:nil] forCellReuseIdentifier:ID];
8.场景需求:在继承中,凡是要求子类重写父类的方法必须先调用父类的这个方法进行初始化操作;建议:父类的方法名后面加上NS_REQUIRES_SUPER; 子类重写这个方法就会自动警告提示要调用这个super方法,示例代码
// 注意:父类中的方法加`NS_REQUIRES_SUPER`,子类重写才有警告提示 - (void)prepare NS_REQUIRES_SUPER;
9.建议书写属性名不要和系统一样,避免发生莫名其妙的问题;特别注意的是label;属性名不要写成
textLabel
10.项目中添加plist类型文件,不要命名为info.plist,以防止和系统自带的文件重名,发生莫名其妙的问题;
11.如果控制器已经加载过,就不用再次加载,优化性能
if (vc.isViewLoaded) return;
12.id类型属性不能用点语法,调用get方法只能用中括号调用,[id 方法名],利用iOS9新特性泛型就可以; 比如数组;
@property (nonatomic,strong) NSMutableArray<DXTopics *> *topicsM;
13.如果不是属性,尽量不要点语法,一个老程序员的建议;
14.使用第三方框架,尽量不要更改内部文件,而应该再次封装,个性定制;
15.判断if书写方式
建议这样写
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) return 44; if (indexPath.row == 1) return 80; if (indexPath.row == 2) return 50; return 44; }
而不是
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { return 44; }else if (indexPath.row == 1){ return 80; }else if (indexPath.row == 2){ return 50; }else{ return 44; } }
16接手一个新项目,快速的调试,查看某个模块或者方法的作用,需要注释掉一个方法,或者某个代码块,直接写
return;而不是全选,注释掉;
比如:查看这个方法
loadNewRecommendTags作用
- (void)loadNewRecommendTags { return; [SVProgressHUD show]; // 取消之前的任务 [self.manager.tasks makeObjectsPerformSelector:@selector(cancel)]; NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[@"a"] = @"tag_recommend"; params[@"c"] = @"topic"; params[@"action"] = @"sub"; [self.manager GET:DXCommonUrlPath parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) { self.recommendTag = [DXRecommendTag mj_objectArrayWithKeyValuesArray:responseObject]; [self.tableView reloadData]; [SVProgressHUD dismiss]; } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { DXLog(@"%@",error); [SVProgressHUD dismiss]; }]; }
17.在一个自定义的View中,或者自定义cell中,modal出一个控制器建议:
[UIApplication sharedApplication].keyWindow.rootViewController
代替
self.window.rootViewController,因为程序可能不止一个window,self.window可能不是主窗口;
18.建议:用CGSizeZero 代替 CGSizeMake(0,0);
CGRectZero代替CGRectMake(0, 0, 0, 0);
CGPointZero代替CGPointMake(0, 0)
19.监听键盘的通知建议:
UIKIT_EXTERN NSString *const UIKeyboardWillChangeFrameNotification
而不是,下面代码;因为键盘可能因为改变输入法,切换成表情输入,切换成英文,那么frame可能会变高,变矮,不一定会发出下面这些通知,但是肯定会发上面的通知
UIKIT_EXTERN NSString *const UIKeyboardWillShowNotification; UIKIT_EXTERN NSString *const UIKeyboardDidShowNotification; UIKIT_EXTERN NSString *const UIKeyboardWillHideNotification; UIKIT_EXTERN NSString *const UIKeyboardDidHideNotification;
20.发布通知的字符串常量规范,建议模仿苹果;如上键盘的通知的书写,加上const 保证字符串不可更改,以Notification结尾,一看就知道是通知;应尽量保证可读性,不要怕句子太长;
NSString *const buttonDidClickNotification = @"buttonDidClickNotification";
21.如果除数为0,iOS8以下会直接报错,(NaN—>Not a Number)iOS9不会,所以应该判断,比如服务器返回图片的宽高,按比例缩放,CGFloat contentH = textW * self.height / self.width;
22.如果声明的属性,只想使用的get方法,不使用set方法,并且不想让外界更改这个属性的值,那么建议在括号里面加readonly;示例:
@property(nonatomic,readonly,getter=isKeyWindow) BOOL keyWindow;
23.如果属性是BOOL类型,建议在括号中重写get方法名称,以提高可读性,示例代码如上;
24.从系统相册中取照片之前,应该判断系统相册是否可用,如果从相机中拍照获取,要判断相机是否可用
// 判断相册是否可以打开 if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) return; // 判断相机是否可以打开 if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) return;
25.在导航控制中,或它的子控制器,设置导航栏的标题应该用self.navigationItem.title = @“标题”而不建议self.title = @“标题”;
26.给cell设置分割线,建议用setFrame:通过设置它高度,设置分割线,而不推荐用给cell底部添加一个高度的UIView,这样做增加了一个控件,从性能上来讲,不如用setFrame设置高度
27.大量操作图层会可能造成应用很卡,给用户体验差,所以尽量不要操作图层;比如设置按钮圆角,比如给button设置圆角;
self.loginBtn.layer.cornerRadius = 5; self.loginBtn.layer.masksToBounds = YES;
28.给分类扩充方法,建议加上前缀,比如第三方框架
SDWebImage,这样做跟系统的方法很容易区分开,减少了程序员之间的沟通成本,同理跟分类添加属性(利用运行时),建议加前缀,以防止苹果官方过一段时间添加了一模一样的属性名,比如给UITextField分类添加了placeholderColor这个属性,万一某天官方给placeholder扩充了这个命名一模一样的属性,那么就不好了
29.凡是在storyboard或者xib中给某个控件添加颜色,颜色对角线有分割线,表示可以设置透明度,如果给这个控件设置透明度建议在这里设置,而不是设置alpha,因为设置了alpha,那么上面有文字也会随着透明度变大,而变得不清楚;可以设置background -->other -->opacity
30.整形转化成浮点型,不建议这么写 a / b 1.0,这样写是错误写法,示例1.5 / 2 1.0;根据运算法则,从作到右,0 1.0 == 0,而应该在前面写1.0 1.5 /2;建议直接强转;(double)a/b;
31.抽取方法,或者写工具类,能写类方法,尽量写成类方法,减少了创建对象的步骤,比如给UIView扩充分类加载xib,viewWithXib;
32.耗时操作应该放在子线程,避免卡主主线程,比如计算文件大小,下载大文件,清除缓存;
33.声明一个属性,如果是对象,比如数组,不能以new单词开始,否则直接报错,因为new在OC中是生成一个对象的方法,有特殊含义;比如,
@property (nonatomic,strong) NSMutableArray<DXTopics *> *newTopicsM;
注意:如果newtopicsM是一个单词(区别于驼峰标志),这样写不会报错;如果是基本数据类型则不会报错,比如
@property (nonatomic,assign) int newNumber;
但是如果一定要写new单词开头的属性,那么声明属性的时候,重写getter方法名称只不过使用getter方法的时候注意下
34.在自定义方法中,
and这个词的用法应该保留。它不应该用于多个参数来说明,就像initWithWidth:height以下这个例子:
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height; 而不应该 - (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
补充部分:
35. 点标记语法属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法。良好的风格:view.backgroundColor = [UIColor orangeColor];[UIApplication sharedApplication].delegate;不良的风格:[view setBackgroundColor:[UIColor orangeColor]];UIApplication.sharedApplication.delegate[/code]
36.布尔类型 因为nil被解析为了NO,所以和nil作比较没有任何的必要。不要将变量和YES直接比较,因为YES被定义为1而BOOL类型是8位的unsigned int,即BOOL的值不仅仅是1或0。
良好的风格: if (!someObject) { } 不良的风格: if (someObject == nil) { }
对于一个BOOL值:两种最佳实践: if (isAwesome) if (![someObject boolValue]) 不良的风格: if ([someObject boolValue] == NO) if (isAwesome == YES) // Never do this. 如果一个BOOL类型的属性名是一个形容词,忽略属性名的“is”前缀是允许的,但需要为访问器指定约定的方法名,比如: @property (assign, getter=isEditable) BOOL editable;
37.单例
应该使用线程安全的模式创建共享的单例实例。+ (instancetype)sharedInstance { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance;}
补充:第一个参数predicate,该参数是检查后面第二个参数所代表的代码块是否被调用的谓词,第二个参数则是在整个应用程序中只会被调用一次的代码块。dispach_once函数中的代码块只会被执行一次,而且还是线程安全的。[/code]
38.枚举类型
当使用enum关键字时,推荐使用苹果最新引入的固定基础类型语法,因为这将获得强类型检查与代码完成功能。SDK现在包含了一个固定基础类型的宏——NS_ENUM()。 NS_ENUM是在iOS6中开始引入的,为了支持之前的iOS版本,使用简单的内联方法: #ifndef NS_ENUM #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type #endif 良好的风格: typedef NS_ENUM(NSInteger, RNCAdRequestState) { RNCAdRequestStateInactive, RNCAdRequestStateLoading };
39.常量 优先使用常类型变量,而不是内嵌的字符串字面值或数字,因为常类型变量能很容易的复用常用的变量值(如π),同时可以快速地修改值而无需查找替换。 常类型变量应该声明为static类型,不要使用#define,除非常类型变量被作为宏使用。良好的风格:static NSString * const RNCAboutViewControllerCompanyName = @"The New York Times Company";static const CGFloat RNCImageThumbnailHeight = 50.0;不良的风格:#define CompanyName @"The New York Times Company"#define thumbnailHeight 2[/code]
40.CGRect函数相较于使用结构体辅助函数(如CGRectMake()函数),优先使用C99结构体初始化语法。 CGRect rect = {.origin.x = 3.0, .origin.y = 12.0, .size.width = 15.0, .size.height = 80.0 }; 当访问CGRect结构体的x、y、width、height成员时,应使用CGGeometry函数,不直接访问结构体成员。良好的风格:CGRect frame = self.view.frame;CGFloat x = CGRectGetMinX(frame);CGFloat y = CGRectGetMinY(frame);CGFloat width = CGRectGetWidth(frame);CGFloat height = CGRectGetHeight(frame);不良的风格:CGRect frame = self.view.frame;CGFloat x = frame.origin.x;CGFloat y = frame.origin.y;CGFloat width = frame.size.width;CGFloat height = frame.size.height;[/code]
41.命名方法和变量的命令应该尽可能做到自描述。良好的风格:UIButton *settingsButton;不良的风格:UIButton *setBut;对于NSString、NSArray、NSNumber或BOOL类型,变量的命名一般不需要表明其类型。[/code]良好的风格:NSString *accountName;NSMutableArray *mailboxes;NSArray *defaultHeaders;BOOL userInputWasUpdated;不良的风格:NSString *accountNameString;NSMutableArray *mailboxArray;NSArray *defaultHeadersArray;BOOL userInputWasUpdatedBOOL;[/code]如果变量不是以上基本常用类型,则变量的命名就应该反映出自身的类型。但有时仅需要某些类的一个实例的情况下,那么只需要基于类名进行命名。NSImage *previewPaneImage; NSProgressIndicator *uploadIndicator; NSFontManager *fontManager; // 基于类名命名[/code]大部分情况下,NSArray或NSSet类型的变量只需要使用单词复数形式(比如mailboxes),不必在命名中包含“mutable”。如果复数变量不是NSArray或NSSet类型,则需要指定其类型。良好的风格:NSDictionary * keyedAccountNames;NSDictionary * messageDictionary;NSIndexSet * selectedMailboxesIndexSet;[/code]由于Objective-C不支持名字空间,为了防止出现命名空间的冲突,在类名和常类型变量名前添加一个由三个大写的字母组成的前缀(如RNC),对于Core Data实体名则可以忽略此规则。如果你子类化了标准的Cocoa类,将前缀和父类名合并是一个很好的做法。如继承UITableView的类可命名为RNCTableView。常类型变量名的书写风格采用驼峰式大小写(第一个单词的首字母小写,其余单词的第一个字母大写。如firstName而不是first_name或firstname。),并使用关联的类名作为其命名前缀,推荐的做法:static const NSTimeInterval RNCArticleViewControllerNavigationFadeAnimationDuration = 0.3;不推荐的做法:static const NSTimeInterval fadetime = 1.7;[/code]
42.方法
一个方法的命名首先描述返回什么,接着是什么情况下被返回。方法签名中冒号的前面描述传入参数的类型。以下类方法和实例方法命名的格式语法: [object/class thing+condition]; [object/class thing+input:input]; [object/class thing+identifer:input];
Cocoa命名举例: realPath = [path stringByExpandingTildeInPath]; fullString = [string stringByAppendingString:@"Extra Text"]; object = [array objectAtIndex:3];
类方法 newString = [NSString stringWithFormat:@"%f",1.5]; newArray = [NSArray arrayWithObject:newString];
良好的自定义方法命名风格: recipients = [email recipientsSortedByLastName]; newEmail = [CDCEmail emailWithSubjectLine:@"Extra Text"]; emails = [mailbox messagesReceivedAfterDate:yesterdayDate];
当需要获取对象值的另一种类型的时候,方法命名的格式语法如下: [object adjective+thing]; [object adjective+thing+condition]; [object adjective+thing+input:input];
良好的自定义方法命名风格: capitalized = [name capitalizedString]; rate = [number floatValue]; newString = [string decomposedStringWithCanonicalMapping]; subarray = [array subarrayWithRange:segment];方法签名尽量做到含义明确。
不良的风格: -sortInfo // 是返回排序结果还是给info做排序 -refreshTimer // 返回一个用于刷新的定时器还是刷新定时器 -update // 更新什么,如何更新 良好的风格: -currentSortInfo // "current" 清楚地修饰了名词SortInfo -refreshDefaultTimer // refresh是一个动词。 -updateMenuItemTitle // 一个正在发生的动作 方法类型修饰符+/-后要放置一个空格,各参数名之间也要放置一个空格。 良好的风格: - (void)setExampleText:(NSString *)text image:(UIImage *)image;如果方法的命名特别长,将方法名拆分成多行。
良好的风格: color = [NSColor colorWithCalibratedHue: 0.10 saturation: 0.82 brightness: 0.89 alpha: 1.00]; 不要将私有的实例变量和方法声明在头文件中,应将私有变量和方法声明在实现文件的类扩展内。
//MyViewController.h文件 @interface MyViewController : UIViewController< UITalbeViewDataSource,UITableViewDelegate> { @private: UITableView *_myTableView; // 私有实例变量 } // 内部使用的属性 @property (nonatomic,strong) NSNumber *variableUsedInternally; - (void)sortName; // 只用于内部使用的方法 @end 良好的风格: //MyViewController.m文件使用类扩展 @interface MyViewController()< UITalbeViewDataSource,UITableViewDelegate> { UITableView *_myTableView; // 外部需要访问的实例变量声明为属性,不需要外部访问的声明为实例变量 NSNumber * variableUsedInternally; } // 从Xcode4.3开始,可以不写方法的前置声明,Interface Builder和Storyboard仍然可以找到方法的定义 @end 构造函数通常应该返回实例类型而不是id类型
43.init与dealloc
dealloc方法应该被放置在实现方法的顶部,直接在@synthesize或@dynamic语句之后。init方法应该被放置在dealloc方法的下面。 init方法的结构看上去应该像这样: - (instancetype)init { self = [super init]; // or call the designated initalizer if (self) { // Custom initialization } return self; }
44.参数 方法参数名前一般使用的前缀包括“the”、“an”、“new”。
良好的风格: - (void) setTitle: (NSString *) aTitle; - (void) setName: (NSString *) newName; - (id) keyForOption: (CDCOption *) anOption - (NSArray *) emailsForMailbox: (CDCMailbox *) theMailbox; - (CDCEmail *) emailForRecipients: (NSArray *) theRecipients;
45.三元运算符长的三元运算符应使用圆括号括起来。三元运算符仅用于赋值和做参数。Blah *a = (stuff == thing ? foo : bar);合并的nil三元运算符应该尽量避免。不良的风格:Blah *b = thingThatCouldBeNil ?: defaultValue;多分支条件应该使用if语句或重构为实例变量。良好的风格:result = a > b ? x : y;不良的风格:result = a > b ? x = c > d ? c : d : y;[/code]
46.条件语句所有的逻辑块必须使用花括号包围,即使条件体只需编写一行代码也必须使用花括号。良好的风格做法:if (!error) { return success;}不良的风格:if (!error) return success;或:if (!error) return success;[/code]
47.异常和错误处理不要在流控制语句中使用异常(NSException)。异常仅用于表明程序员的错误。为了表明一个错误,使用NSError *。当一个方法通过引用返回一个错误参数,应该检测返回值的状态,而不是错误参数的状态。良好的风格:NSError *error;if (![self trySomethingWithError:&error]) { // Handle Error}不良的风格:NSError *error;[self trySomethingWithError:&error];if (error) { // Handle Error}[/code]在方法执行成功的情况下赋值非Null值给错误参数,会使路径跳转到假条件分支(随后程序奔溃)。
感谢原作者!转自于:http://www.jianshu.com/p/71fdd1ae714c
相关文章推荐
- ios开发 ad hoc
- iOS 视图,动画渲染机制探究
- iOS 原生地图定位
- iOS中的KVO使用
- 文章标题
- iOS开发多线程篇—GCD的基本使用
- iOS设备远程Push功能详解
- ios开发--一个苹果证书怎么多次使用——导出p12文件
- iOS使用正则表达式选取数字和文字不同颜色
- iOS和JS之间的交互
- iOS-MultipeerConnectivity框架开发(一)
- iOS mask 层和 CAShapeLayer层中间挖去一部分的 源码分析
- iOS开发--应用程序上线
- Ios设计模式
- IOS 手机定位
- IOS-Touch ID的简单使用
- iOS CAShaperLayer 层详解
- iOS 有关推送实现过程
- ios 的 字段、方法、属性的访问权限控制
- iOS将GIF转变为图片