- 驼峰命名法:用的最广的命名法,变量名常用此命名,命名由一个单词或多个单词组合而成,首字母小写其余单词首字母大写,如:“userName”; 下划线命名法:每个单词间使用下划线“_”分割,所有字母均小写,如:“user_name”;
- 帕斯卡命名法:每个单词的首字母均大写的一串字符,与“骆驼命名法”的区别在于前者的首字母大写,后者的首字母小写。如:“UserName”。
- 普通变量(修饰+类型)
12
@property (nonatomic, strong) UILabel *titleLabel; //表示*标题*的label,是*UILabel*类型 @property (nonatomic, strong) UIButton *confirmButton; //表示*确认*的button,是*UIButton*类型 - 如果是声明BOOL类型,建议在括号中重写get方法1@property (nonatomic, readonly, getter = isKeyWindow) BOOL keyWindow;
NS_ENUM
和NS_OPTIONS
宏来定义枚举类型- 在常量前边加上字母k作为标记static const NSTimeInterval kAnimationDuration = 0.3
- 一些公开的常量通常使用类名作为前缀,同样是避免命名冲突而引发问题。案例:
-
UIKIT_EXTERN NSString *const UIApplicationDidEnterBackgroundNotification; // 常量命名
- 通知命名 使用const修饰,以Notification结尾
通知命名规则: [触发通知的类名] + [Did | Will] + [动作] + Notification错误示例:UIKIT_EXTERN NSString *const textFieldTextBeginEditingNotification;UIKIT_EXTERN NSString *const textFieldTextEndEditingNotification;正确操作:UIKIT_EXTERN NSString *const UITextFieldTextDidBeginEditingNotification;UIKIT_EXTERN NSString *const UITextFieldTextDidEndEditingNotification;
ps:这里面需要注意的是变量名尽量不要使用缩写,如我们经常可以看到很多开发者习惯于把根视图控制器写成rootVC或者mainVC等等,而系统给我们提供的却是完整的命名:self.window.rootViewController,假如系统给我们提供的是self.window.rootVC这种形式,以及其他命名方式也这样以非专业词汇的缩写命名,相信很多开发者会看的一头雾水。
- 要什么表示取得某个对象,要以名词作为方法的开头如:“string”,“data”,“image”等,案例:
- (NSRange)rangeOfString:(NSString *)searchString; // 表明获取一个range
- (UIImage *)imageNamed:(NSString *)name;
- 做什么表示执行某种操作,要以动词作为方法开头
-
- (NSArray<ObjectType> *)sortedArrayUsingSelector:(SEL)comparator; // 表明目的是排序
- (void)setUpNavBar
- 如果该方法需要参数,每个参数前最好添加参数提示。如下面两种代码对比。
- (instancetype)init:(CGRect)frame; // 糟糕的方法命名- (instancetype)initWithFrame:(CGRect)frame; // 好的方法命名
- 如果该方法需要多个参数,不能使用and这个单词连接参数
1
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
- 一些代表过程监听的方法可能以“谁执行什么过程”这种形式命名,且动作发生之前通常使用“Will”,发生之后使用“Did”,询问是否发生使用“Should”。案例:
123
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; //将要选择这一行cell- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; //已经选择这一行cell- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); //是否高亮(选择这一行cell)
- 回调方法第一个参数是调用者
1
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;- (void)buttonTapped:(UIButton*)sender;
- 类目方法:如果我们在编码中使用类目给一些系统的类拓展方法,那么推荐给这些方法添加前缀,目的很简单,就是避免和这个类的私有方法或者将来系统可能拓展的方法出现方法名相同的冲突。如我当初在写夜间模式的demo的时候给UIView写了一个类目,拓展的方法名我使用了姓名的缩写“jxl”作为方法的前缀:
-
- (void)jxl_setDayMode:(DAY_AND_NIGHT_MODE_BLOCK)dayMode nightMode:(DAY_AND_NIGHT_MODE_BLOCK)nightMode;
- 返回BOOL值得方法加前缀is,has 1 2- (BOOL)isEqualToString:(NSString *)aString; - (BOOL)hasPrefix:(NSString *)aString;
在编写代码时,我们常用#define去定义一个宏。我们定义一个播放动画的时间为常量,可能这样定义:
#define ANIMATION_DURATION 1.0
上述预处理指令会把源代码中的 ANIMATION_DURATION 替换为1.0,可以达到效果,不过这样定义是存在问题的,定义出来的常量没有类型信息,无法一眼看出代表的是一个时间,可读性差,而且如果把这个宏定义放在头文件中的话,那么引入了这个头文件的代码,其 ANIMATION_DURATION 都会被替换,如果有人定义了常量值,这将导致应用程序中的常量值不一致。一种更好的处理方案是使用类型常量替换掉相应的#define预处理指令。
-
外部不可见:
.m文件中:
static const NSTimeInterval kAnimationDuration = 1.0;// 命名规则:不被外部访问时 k+变量名
static修饰:意味着仅在此编译单元(.m文件)中可见;const修饰:如果试图修改值,编译器就会报错;static const:二者都使用,编译器的处理效果和#define一样,把遇到的变量替换为常值。
- 外部可见: 有些时候是需要向外部公开某个常量的,For example,在使用通知中心的时候,你需要向其他对象派发通知,监听者需要知道监听的事件,这个事件的名称我们通常写成一个外界可见的常值变量,这样的话,监听者无需知道实际字符串的值,只需要以常值变量来作为自己监听的事件名称,系统的 UIApplicationDidEnterBackgroundNotification, UIApplicationWillEnterForegroundNotification等都是这样做的: 在.h文件中:
NSString *const MyClassNameActionNameNotification; // 命名规则:类名+事件名+Notification
ps:从右至左解读,“一个常量,而这个常量是一个指针,指向NSString对象”。 在.m文件中:
NSString *const MyClassNameActionNameNotification = @"MyClassNameActionNameNotification";
- 本例中的写法为: 在.h文件中:
const NSTimeInterval MyClassNameAnimationDuration;// 命名规则:类名+变量名
在.m文件中:
const NSTimeInterval MyClassNameAnimationDuration = 0.3;
当我们想要表示某一种设置的多个选项或者多种状态时,推荐使用枚举。枚举的意义本来就是将一些表示某一种设置或者状态的数字转化成方便开发者阅读的形式,极大的提高了可读性。以上面其他的命名规则话题中提到过的枚举为例:
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { UIViewAnimationTransitionNone, // 枚举值命名 UIViewAnimationTransitionFlipFromLeft, UIViewAnimationTransitionFlipFromRight, UIViewAnimationTransitionCurlUp, UIViewAnimationTransitionCurlDown, };
FooViewController () < UITableViewDataSource, // 你的注释 UITableViewDelegate // 你的注释 >@end
// Methods...#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate
#pragma mark - CustomDelegate
#pragma mark - Getters and Setters
#pragmamark - Notification
//Methods...
#pragmamark - Request Methods
//Methods...
错误示范:@implementation ViewController { UIButton *authCodeBtn; } - (void)viewDidLoad { [super viewDidLoad]; UIButton *_loginBtn = [[UIButton alloc] init]; } 好的习惯:@implementation ViewController { UIButton *_authCodeBtn; } - (void)viewDidLoad { [super viewDidLoad]; UIButton *loginBtn = [[UIButton alloc] init];
// 注意:父类中的方法加`NS_REQUIRES_SUPER`,子类重写才有警告提示
12345 | view.backgroundColor = [UIColor redColor]; [UIApplication sharedApplication].delegate; //推荐 [view setBackgroundColor:[UIColor redColor]]; UIApplication.sharedApplication.delegate; //不推荐 |
1 | @property (nonatomic, readwrite, copy) NSString *name; |
- 如果是内部使用的属性, 需定义成该类的私有属性(写在.m文件的class extension里)
- 对于拥有Mutable子类型的对象, 例如NSString NSArray NSDictionary NSDictionary, 一定要定义成copy属性
- 尽量不要暴露mutable类型的对象在public interface, 建议在.h定义一个Inmutable类型的属性, 然后在.m的get函数里面返回一个内部定义的mutable变量
- 不要出现混合声明,尽可能都使用@property声明
字面量语法实际上是一种“语法糖”(syntactic sugar),以一种非常简单快捷的方式能创建对象,使我们开发者编程更高效,更愉悦。目前Objective-C支持的字面量语法的类有NSString,NSNumber, NSArray, NSDictionary。使用字面量语法的好处:
-
使用字面量语法可以缩减源代码长度,没有多余语法成分,提高可读性;
-
在使用字面量语法创建数组时,如果数组元素对象中有nil,则会抛出异常,其效果等于先创建一个数组,然后把方括号内的所有对象都加到这个数组中。抛出的异常会是这样:
***Terminating app due to uncaught exception‘NSInvalidArgumentException', reason:’***-[__NSPlaceholderArray initWithObjects:count:] : attempt toinsert nil object from objects[0]'
NSArray *arrayA = [NSArray arrayWithObjects:object1, object2, object3, nil];
针对上面代码进行分析,如果object2=nil;,arrayA数组可以创建,但只有object1一个对象,因为“+ (instancetype)arrayWithObjects:”方法会一次处理各个参数,直到发现nil为止,而arrayB会抛出异常,这个特性使我们更容易发现程序中存在的问题,提高了安全性。ps:字典跟数组一样,一旦有值为nil,也会抛出异常,而且创建时的“键”“值”顺序和我们正常说的“键值”顺序一样(正常初始化为“值”“键”),便于阅读。
使用字面量语法的缺点:使用字面量创建都是不可变对象,如果想创建可变对象需要复制一份:
NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy];
12345678 | if (obj) { //... }if (!obj) { //... } |
错误示例: Bool isAdult;if (age > 18){ isAdult = YES; } else { isAdult = NO; }// 好的习惯 Bool isAdult; isAdult = age > 18;
1 | BOOL isAdult = age > 18; |
12345678910111213141516 | if ([self canDeleteAccount:account]) { //... } /** method*/ - (BOOL)canDeleteAccount:(account) { if (account.balance == 0 || account.owner.isDead == YES || account.isCancel == YES) { return YES; } else { return NO; } } |
错误示例:if(userName.length){ if (passWord.length) { //可以登录 } } 好的习惯:if(!userName.length){ return; };if(!passWord.length){ return; };
1 23 | if (!user.account) return NO;if (!user.password) return NO;return YES; |
1 | [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([UserListCell class]) bundle:nil] forCellReuseIdentifier:ID]; |
1 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ if (indexPath.row == 0) return 40; if (indexPath.row == 1) return 50; if (indexPath.row == 2) return 60; return 44;} |
1 | - (void)exampleFunctionWithPara:(int)para1 anotherPara:(int)para2 theThirdPara:(int)para3 theFourthPara:(int)para4{ ...} |
1 | [self exampleFunctionWithPara:1 anotherPara:2 theThirdPara:3 theFourthPara:4 theFifthPara:5]; |
好的习惯: sum = value1 + value2;//瞬间 高大上UILable *lbl = [[UILable alloc] init] ;
:
前后要留有一个空格 错误示范: NSDictionary *attributs = @{ NSForegroundColorAttributeName:[UIColor orangeColor], NSFontAttributeName:[UIFont systemFontOfSize:12] }; 正确示例: NSDictionary *attributs = @{ NSForegroundColorAttributeName : [UIColor orangeColor], NSFontAttributeName : [UIFont systemFontOfSize:12] };
错误示例: UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(10, 0, 100, 40)]; [self.view addSubview:phoneTf]; UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(10, 110, 100, 40)]; [self.view addSubview:passwordTf];// 正确的方式: CGFloat margin = 10; CGFloat width = 100; CGFloat height = 40; UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, 0, width, height)]; [self.view addSubview:phoneTf]; UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, CGRectGetMaxY(phoneTf.frame) + margin, width, height)]; [self.view addSubview:passwordTf];
__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 = ^{
if (strongSelf) { [strongSelf doSomething]; // strongSelf != nil// preemption, strongSelf still not nil(抢占的时候,strongSelf 还是非 nil 的) [strongSelf doSomethingElse]; // strongSelf != nil }else { // Probably nothing... return; } };