- 普通变量(修饰+类型)
1
2
|
@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这种形式,以及其他命名方式也这样以非专业词汇的缩写命名,相信很多开发者会看的一头雾水。
4.宏命名规则
通常会把单词的所有字母大写,目的是为了告诉开发者这是一个宏,而不是一个普通的变量,当然如果宏的名称如果由多个单词组成,通常是每个单词之间使用下划线分割开,如:“JXL_ABC_DEF”这种形式。
5.方法命名规则
大部分方法可以分为两类:要什么 和 做什么
- 回调方法第一个参数是调用者
1
|
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- (void)buttonTapped:(UIButton*)sender;
|
- 返回BOOL值得方法加前缀is,has
1
2
|
- (BOOL)isEqualToString:(NSString *)aString;
- (BOOL)hasPrefix:(NSString *)aString;
|
二 代码编写规范
1.引用头文件
类的头文件尽量不要引用其他头文件,无需知道类的内部细节使用@class即可
2.使用类型常量替换#define预处理指令
在编写代码时,我们常用#define去定义一个宏。我们定义一个播放动画的时间为常量,可能这样定义:
#define ANIMATION_DURATION 1.0
上述预处理指令会把源代码中的 ANIMATION_DURATION 替换为1.0,可以达到效果,不过这样定义是存在问题的,定义出来的常量没有类型信息,无法一眼看出代表的是一个时间,可读性差,而且如果把这个宏定义放在头文件中的话,那么引入了这个头文件的代码,其 ANIMATION_DURATION 都会被替换,如果有人定义了常量值,这将导致应用程序中的常量值不一致。一种更好的处理方案是使用类型常量替换掉相应的#define预处理指令。
static const NSTimeInterval kAnimationDuration = 1.0;// 命名规则:不被外部访问时 k+变量名
static修饰:意味着仅在此编译单元(.m文件)中可见;const修饰:如果试图修改值,编译器就会报错;static const:二者都使用,编译器的处理效果和#define一样,把遇到的变量替换为常值。
-
外部可见:
有些时候是需要向外部公开某个常量的,For example,在使用通知中心的时候,你需要向其他对象派发通知,监听者需要知道监听的事件,这个事件的名称我们通常写成一个外界可见的常值变量,这样的话,监听者无需知道实际字符串的值,只需要以常值变量来作为自己监听的事件名称,系统的 UIApplicationDidEnterBackgroundNotification, UIApplicationWillEnterForegroundNotification等都是这样做的:
在.h文件中:
UIKIT_EXTERN NSString *const MyClassNameActionNameNotification; // 命名规则:类名+事件名+Notification
ps:从右至左解读,“一个常量,而这个常量是一个指针,指向NSString对象”。
在.m文件中:
NSString *const MyClassNameActionNameNotification = @"MyClassNameActionNameNotification";
UIKIT_EXTERN const NSTimeInterval MyClassNameAnimationDuration;// 命名规则:类名+变量名
在.m文件中:
const NSTimeInterval MyClassNameAnimationDuration = 0.3;
3.用枚举表示设置或状态
当我们想要表示某一种设置的多个选项或者多种状态时,推荐使用枚举。枚举的意义本来就是将一些表示某一种设置或者状态的数字转化成方便开发者阅读的形式,极大的提高了可读性。以上面其他的命名规则话题中提到过的枚举为例:
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone, // 枚举值命名
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
这个枚举含有5个值,分别表示了5种动画状态,如果系统以“0”,“1”,“2”等这样的数字来表示状态的话,作为开发者想要知道每个数字代表什么样的动画效果只能一个一个的去测试,并且需要记住每一个数字代表什么状态,方便以后使用,那么我相信绝大多数开发者都会疯掉的。
4.协议的签订格式
协议的签订推荐下面这种写法,这种写法的好处是你签订了什么协议一目了然,而且后面填写注释看起来也会更加舒服。
@interface FooViewController ()
<
UITableViewDataSource, // 你的注释
UITableViewDelegate // 你的注释
>
@end
5.代码整理
为了让你的代码更整洁,你需要将你的代码做好归类整理,例如一个ViewController实现文件里的代码可能是这样:
#pragma mark -
Life Cycle
// Methods...
#pragma mark - UITableViewDataSource
// Methods...
#pragma mark - UITableViewDelegate
// Methods...
#pragma mark - CustomDelegate
// Methods...
#pragma mark -
Private Methods
// Methods…(.m中声明)
#pragma mark -
Public Methods
// Methods…(.h中声明)
#pragma mark -
Getters and Setters
// Methods...
#pragmamark
- Notification
//Methods...
#pragma mark -
Event Response
// Methods...
#pragmamark
- Request Methods
//Methods...
6.实例变量声明时变量名前面加下划线“_”,局部变量不用加
错误示范:
@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];
}
7.场景需求:在继承中,凡是要求子类重写父类的方法必须先调用父类的这个方法进行初始化操作;建议:父类的方法名后面加上NS_REQUIRES_SUPER;
子类重写这个方法就会自动警告提示要调用这个super方法,示例代码
// 注意:父类中的方法加`NS_REQUIRES_SUPER`,子类重写才有警告提示
- (void)prepare NS_REQUIRES_SUPER;
8.成员变量 setter&getter
一些刚刚入门的iOS开发者总是纠结在给属性赋值或者取值的时候,究竟是直接操作成员变量还是通过使用setter或者getter进行赋值取值操作,关于这个问题很多人的观点也不相同,从性能上来说,由于Objective-C的消息机制使用setter和getter效率要比直接操作成员变量的效率低,从内存方面来说,setter和getter都有内存的处理,所以使用起来更安全,一个折中的办法是除Lazy
Loading对象外,赋值时使用setter,取值时直接操作成员变量,这样既保障了内存的安全,又提升了效率,而且通常我们的取值操作又多于赋值操作。
尽可能使用点语法访问属性,但是访问其他实例对象使用括号
1
2
3
4
5
|
view.backgroundColor = [UIColor redColor];
[UIApplication sharedApplication].delegate; //推荐
[view setBackgroundColor:[UIColor redColor]];
UIApplication.sharedApplication.delegate; //不推荐
|
9.定义属性尽可能写全参数
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声明
10.使用字面量语法替换等价方法
字面量语法实际上是一种“语法糖”(syntactic sugar),以一种非常简单快捷的方式能创建对象,使我们开发者编程更高效,更愉悦。目前Objective-C支持的字面量语法的类有NSString,NSNumber, NSArray, NSDictionary。使用字面量语法的好处:
***Terminating app due to uncaught exception
‘NSInvalidArgumentException', reason:’***
-[__NSPlaceholderArray initWithObjects:count:] : attempt to
insert nil object from objects[0]'
NSArray *arrayA = [NSArray arrayWithObjects:object1, object2, object3, nil];
NSArray *arrayB = @[object1, object2, object3];
NSNumber *isHide = @NO;
NSNumber *errorCode = @404;
针对上面代码进行分析,如果object2=nil;,arrayA数组可以创建,但只有object1一个对象,因为“+ (instancetype)arrayWithObjects:”方法会一次处理各个参数,直到发现nil为止,而arrayB会抛出异常,这个特性使我们更容易发现程序中存在的问题,提高了安全性。ps:字典跟数组一样,一旦有值为nil,也会抛出异常,而且创建时的“键”“值”顺序和我们正常说的“键值”顺序一样(正常初始化为“值”“键”),便于阅读。
使用字面量语法的缺点:使用字面量创建都是不可变对象,如果想创建可变对象需要复制一份:
NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy];
11.判断nil或者YES/NO
1
2
3
4
5
6
7
8
|
if (obj)
{
//...
}
if (!obj)
{
//...
}
|
12.BOOL类型赋值
错误示例:
Bool isAdult;
if (age > 18){
isAdult = YES;
} else {
isAdult = NO;
}
//
好的习惯
Bool isAdult;
isAdult = age > 18;
1
|
BOOL isAdult = age > 18;
|
13. 复杂的条件判断
如果判断较为复杂,尽可能写到一个方法里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
if ([self canDeleteAccount:account])
{ //... }
/**
method
*/
- (BOOL)canDeleteAccount:(account)
{
if (account.balance == 0
|| account.owner.isDead == YES || account.isCancel == YES)
{
return YES;
}
else
{
return NO;
}
}
|
14.嵌套判断
错误示例:
if(userName.length){
if (passWord.length) {
//可以登录
}
}
好的习惯:
if(!userName.length){ return; };
if(!passWord.length){ return; };
|
if (!user.account) return NO;
if (!user.password) return NO;
return YES;
|
15.加载xib
加载xib名称使用 NSStringFromClass()
1
|
[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([UserListCell class]) bundle:nil] forCellReuseIdentifier:ID];
|
16.判断if书写方式
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;
}
|
17.对于参数很多的函数,参数各占一行,并以冒号对齐,如:
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];
|
18.运算符号间要留有合适的间隔
好的习惯:
sum = value1 + value2;//瞬间 高大上
UILable *lbl = [[UILable alloc] init] ;
19.字典构造时的注意点 : 前后要留有一个空格
错误示范:
NSDictionary *attributs = @{
NSForegroundColorAttributeName:[UIColor orangeColor],
NSFontAttributeName:[UIFont systemFontOfSize:12]
};
正确示例:
NSDictionary *attributs = @{
NSForegroundColorAttributeName : [UIColor orangeColor],
NSFontAttributeName : [UIFont systemFontOfSize:12]
};
20.对一些相同的东西避免写死,特别是控件的frame
不便于修改
错误示例:
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];
21.避免循环引用
如果【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(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil(抢占的时候,strongSelf 还是非 nil 的)
[strongSelf doSomethingElse]; // strongSelf != nil }
else { // Probably nothing... return;
}
};
weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。
22.建议:
用CGSizeZero 代替 CGSizeMake(0,0);
CGRectZero代替CGRectMake(0, 0, 0, 0);
CGPointZero代替CGPointMake(0, 0)
23.在导航控制中,或它的子控制器,设置导航栏的标题应该用self.navigationItem.title = @“标题”而不建议self.title
= @“标题”;
|