1:多用类型常量,少用#define预处理指令。
举例定一个动画播放时间,使用预处理指令定义,#define ANIMATION_DURATION 0.3,这条预处理指令会把源码中ANIMATION_DURATION替换为0.3,这样定义出来的常量没有类型信息,此外预处理过程会把碰到的所有ANIMATION_DURATION都替换为0.3,这样的话假设此指令声明在某个头文件中,那么所有引入这个头文件的代码,其ANIMATION_DURATION都会被替换。
下面这种定义方式更好 更安全
static const NSTime Interval kAnimationDuration = 0.3;请注意用此方式定义的常量包含类型信息,其好处是清楚的扫描了常量的含义。由此可知该常量类型为NSTimeInterval,这有助于为其编写开发文档,这种方式能让人更容易理解。常用的命名方法是:若常量局限于某个编译单元,也就是实现文件之内,则在前面加字母K;若常量在类之外可见,则通常以类名为前缀。
变量定义一定要同时用static const,如果试图修改由const修饰符所声明的变量,那么编译器就会报错。static修饰符意味着该变量仅在定义此变量的编译单元中可见。(编译单元指每个类的实现文件即.m文件)
有时候需要对外公开某个常量。比方说在类中调用NSNotificationCenter,以通知他人,用一个对象来通知他人。用一个对象来派发通知,令其他欲接受通知的对象向该对象注册,这样就能实现此功能了。派发通知时,需要使用字符串来表示此通知的名称,而这个名称可以声明为一个外界可见的常值变量,这样的话注册者无需知道实际字符串的值,只需要以常值变量来注册自己想要接受的通知即可。
此类常量需放在全局符号表中,以便可以在定义该常量的编译单元之外使用。
应该这样来定义
in the header file
extern NSString *const EOCStringConst;
in the implementation file
NSString *const EOCStringConst = @"value";
这个常量在头文件中声明,且在实现文件中定义。
注意常量的名称,为避免名称冲突最好用与之相关的类名做前缀。
假如要把EOCAnimatedView中的动画播放时间对外公布,那么可以按如下定义
EOCAnimatedView.h中
extern const NSTimerInterval EOCAnimatedViewAnimationDuration;
EOCAnimatedView.m中
const NSTimerInterval EOCAnimatedViewAnimationDuration = 0.3;
这样定义要优于使用预处理指令,因为编译器会确保常量值不变,一旦修改就会报错。
用前缀避免命名空间冲突
Objective-C没有其他语言那种内置的命名空间。鉴于此,我们在起名的时候要设法避免潜在的命名冲突,否则就很容易重名了。如果发生命名冲突那么应用程序的链接过程就会出错,因为其中出现了重复的符号。避免此问题的唯一办法就是变相实现命名空间,为所有名称都加上前缀。所选前缀可以是与公司,应用程序或二者皆有关联之处。比方说你所在的公司叫做Effective Widgets,那么九可以在所有应用程序中会用到的那部分代码中使用EWS作前缀。不仅是类名,应用程序中的所有名称都应加前缀,如果为既有类新增分类,那么一定要给分类即分类中的方法加上前缀,切前缀最好是三个大写字母。
实现description方法
在自定义的类中实现description方法,返回对自己有用的信息,方便在调用NSLog方法打印与类有关信息时,输入对自己有用的信息。
- (NSString *)description{
return [NSString stringWithFormat:@"%@: %p",[self class],self];
}
NSObject类还有一个方法在调试的时候用得到debugDescription, debugDescription是开发者在调试器中以控制台命令po打印对象时才调用。
- (NSString *) debugDescription{
return [NSString stringWithFormat:@"%@: %p",[self class],self];
}
尽量使用不可变对象
设计类的时候,应充分利用属性来封装数据。而在使用属性时则可将其声明为只读属性(readOnly)。默认情况下属性时可读可写的(readWrite),这样设计出来的类都是可变的。但是具体到编程中,应尽量把对外开发的属性设计为只读属性,只有在确有必要的时候,才设置可读可写的属性。只读属性只会为属性创建get方法,如果有人试着改变属性,那么编译器就会报错。
但是有时候可能想修改封装在对象内部的数据,但是却不想令这些数据为外人所动。这种情况下,通常做法是在对象内部将属性重新声明为readWrite,这一操作可于“class-continuation”中完成,在公共接口中声明的属性可于此处重新声明,属性的其他特质必须保持不变。举例:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface EOCEmployee : NSObject
@property (nonatomic,copy,readonly) NSString *name;
@property (nonatomic,assign,readonly) NSInteger salary;
@property (nonatomic,strong,readonly) NSSet *friends;
- (id)initWithName:(NSString *)name andSalary:(NSInteger)salary;
- (void)addFriends:(EOCEmployee *)employee;
- (void)removeFriends:(EOCEmployee *)employee;
@end
在class-continutation中可以这样写
#import "EOCEmployee.h"
@interface EOCEmployee()
@property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,assign,readwrite) NSInteger salary;
@end
@implementation EOCEmployee{
NSMutableSet *_internalFriends;
}
屏幕快照 2019-06-28 上午10.51.32.png
#import "EOCEmployee.h"
@interface EOCEmployee()
@property (nonatomic,copy,readwrite) NSString *name;
@property (nonatomic,assign,readwrite) NSInteger salary;
@end
@implementation EOCEmployee{
NSMutableSet *_internalFriends;//定义一个可变的collection方便内部操作修改。
}
- (id)initWithName:(NSString *)name andSalary:(NSInteger)salary{
self = [super init];
if (self) {
_name = name;
_salary = salary;
_internalFriends = [NSMutableSet new];
}
return self;
}
- (void)addFriends:(EOCEmployee *)employee{
[_internalFriends addObject:employee];
}
- (void)removeFriends:(EOCEmployee *)employee{
[_internalFriends removeObject:employee];
}
//(浅拷贝)返回一个不可变的collection
- (NSSet *)friends{
return [_internalFriends copy];
}
@end
总结:1.尽量创建不可变对象2.若某属性仅可于对象内部修改,则在“class-continuation”中将其由readonly属性重新声明为readwrite属性。3.不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection。
给私有方法加前缀
给私有方法加前缀,这样有助于区分私有方法与对外公开的方法。最好使用p+下划线的方式,这样很容易看出是私有方法。
- (void)p_privateMethod{
do anything;
}
理解NSError
NSError对象里封装了三条信息
屏幕快照 2019-06-28 下午4.04.35.png
网友评论