题目:
尝试修改以下代码中的风格错误:
———————————————————我是分割线——————————————————–
博主在平时code时一直很注重自己的代码风格,固在修改此题可以说是信心十足,但修改下来也就发现9处错误,还只是一些浅显的“硬伤”,没有什么技术含量,而结合网上的一些错误修改却发现十多条的“可优化部分”!
真是平时没有严格要求住自己,才会让自己把一些错误的事情当做正确的事情来执行!
———————————————————我是分割线——————————————————–
先附上网上的一种优秀的修改方法 (稍加修改版):
typedef NS_ENUM(NSInteger, DBSex) {
DBSexMan,
DBSexWoman
};
@interface DBUser : NSObject<NSCopying>
@property (nonatomic, copy , readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) DBSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(DBSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
@end
下面对具体修改的地方,分两部分做下介绍:硬伤部分和优化部分
。因为硬伤部分没什么技术含量,为了节省大家时间,放在后面讲,大神请直接看优化部分。
可优化部分(代码由上往下看):
1、enum
enum官方给出的推荐方法中已经摒弃简单的typedef enum
这种写法了,而是用NS_ENUM
和 NS_OPTIONS
宏来定义枚举类型,参见官方的 Adopting Modern Objective-C 一文:
举例:
//定义一个枚举
typedef NS_ENUM(NSInteger, DBSex) {
DBSexMan,
DBSexWoman
};
2、一般类名需要在加上前缀,用以分割项目中的所属模块或者与第三方类区分
3、熟悉定义的时候优于原子属性(nonatomic)
在绝大多数情况下是需要的,所以统一写在第一位,观察下面有该类的初始化方法,固猜测此此类中属性是不允许随意被外界修改的(需要修改需要有特定的方法),所以在属性中需要加上readonly
属性。
得出修改结果:
@property (nonatomic, copy , readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readonly) DBSex sex;
注:后面的文章会说到为什么要把strong改成copy,大概可以理解为指针本身的地址
和指针指向的地址
的区别,说白了就是浅复制和深复制的区别。
4、NSInteger
、NSUInteger
、NSTimeInterval
、CGFloat
、int
、float
、unsigned
int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
动画时间 -> NSTimeInterval
从命名所表达的意思来看,属性age应该代表的是岁数,而岁数为非负整数,所以使用NSUInteger优于int。
这样做的是基于64-bit 适配考虑,详情可参考出题者的博文《64-bit Tips》
5、方法命名不规范
-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;
观察方法名,不难理解到此方法适用于用参数来初始化一个实力DBUser
方法,所以:
1) 返回类型id
改为instancetype
会更加明了;
2) 名称用init
足以表达意思initUserModel
会略显臃肿;
3) 参数需要遵循原则:**with连接第一个独立个参数,相对独立参数用and连接,并列参数不要任何任何动词连接。**
(此处仅代表博主个人意见,如有不同,请指出!)
//错误,不要使用"and"来连接参数
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
//错误,不要使用"and"来阐明有多个参数
- (instancetype)initWithName:(CGFloat)width andAge:(CGFloat)height;
//正确,使用"and"来表示两个相对独立的操作
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
4) 初始化时漏掉参数sex
,在不太改动原来方法的情况下,应该拆分为两个独立的方法。
最后得出结果:
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(DBSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
doLogIn方法命名不规范:添加了多余的动词前缀。 请牢记:
如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用do,does这种多余的关键字,动词本身的暗示就足够了。
6、MVC而非MVVM
由于博主代码风格一值都是使用MVVM,所以对此不做过多评价,所以贴出大神原话:
doLogIn方法不应写在该类中:虽然LogIn的命名不太清晰,但笔者猜测是login的意思,而登录操作属于业务逻辑,观察类名UserModel,以及属性的命名方式,应该使用的是MVC模式,并非MVVM,在MVC中业务逻辑不应当写在Model中。(如果是MVVM,抛开命名规范,UserModel这个类可能对应的是用户注册页面,如果有特殊的业务需求,比如:login对应的应当是注册并登录的一个Button,出现login方法也可能是合理的。)
注:即使是在MVVM中,login这种方法也不应该出现再次出,此类偏向于数据模型,并不属于业务逻辑范畴。
硬伤部分
1.在-和(void)之间应该有一个空格
2.enum中驼峰命名法和下划线命名法混用错误:枚举类型的命名规则和函数的命名规则相同:命名时使用驼峰命名法,勿使用下划线命名法。
3.enum左括号前加一个空格,或者将左括号换到下一行
4.enum右括号后加一个空格
5.UserModel :NSObject 应为UserModel : NSObject,也就是:右侧少了一个空格。
6.@interface与@property属性声明中间应当间隔一行。
7.两个方法定义之间不需要换行,有时为了区分方法的功能也可间隔一行,但示例代码中间隔了两行。
8.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格。而且- 与(id)之间少了空格。
9.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中方法名与参数之间多了空格:(NSString*)name前多了空格。
10.-(id)initUserModelWithUserName: (NSString*)name withAge:(int)age;方法中(NSString*)name,应为(NSString *)name,少了空格。
11.doLogIn方法命名不清晰:笔者猜测是login的意思,应该是粗心手误造成的。
12.第二个@property中assign和nonatomic调换位置。