使用Cocoa 创建应用程序时一定要注意,Apple 宣称其保留使用所有“两字母前缀” (two-letter prefix)的权利,所以你自己选用的前缀应该是三个字母的。
// 公司/应用程序相关的类名前缀
// 公司前缀
@interface ABCCompanyPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)work;
@end
// 应用程序前缀
@interface MyAppViewController : UIViewController
@property (nonatomic, strong) ABCCompanyPerson *person;
@end
// 第三方库的类名前缀
// 第三方库前缀
@interface TPThirdPartyManager : NSObject
+ (instancetype)sharedManager;
- (void)performTask;
@end
// 你的程序库,使用了第三方库
@interface YourLibraryClass : NSObject
@property (nonatomic, strong) TPThirdPartyManager *manager;
- (void)doSomething;
@end
所有对象均要初始化。初始化时,有些对象可能无须开发者向其提供额外信息,不过一般来说还是要提供的。以iOS的UI框架UIKit为例,其中有个类叫做UITableViewCell,初始化该类对象时,需要指明其样式及标识符,标识符能够区分不同类型的单元格。由于这种对象的创建成本较高,所以绘制表格时可依照标识符来复用,以提升程序效率。我们把这种可为对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”。(实际上就是在初始化时直接提供需要的数据)
全能初始化
要点
调试程序时,经常需要打印并查看对象信息。一种办法是编写代码把对象的全部属性都输出到日志中。不过最常见的做法还是像下面这样:
NSLog(@"object = %@", object);
在构建需要打印到日志的字符串时,object对象会收到description消息,该方法所返回的描述信息将取代“格式字符串”里的%@。比方说,object是个数组,若用下列代码打印其信息:
NSArray *object = @[@"A string", @(123)];
NSLog(@"object = %@", object);
输出:
object = {
"A string"
123
}
如果是一个自定义的类,则输出:
object = <EOCPerson: 0x7fd9a1600600>
这时候就需要我们重写description
方法
例如下:
- (NSString *) description {
return [NSString stringWithFormat:@"<%@: %p, %@>",
[self class],
self,
@{@"latitude":_title,
@"latitude":@(_latitude),
@"longitude":@(_longitude)}
];
}
输出:
location = <EOCLocation: 0x7f98f2e01d20, {
latitude = "51.506"
longitude = 0;
title = London;
}>
debugDescription:
这个也是一种描述方法,和description差不多,就是描述的位置不一样,description是在函数调用类的时候触发方法才输出的,而debugDescription是在控制台中使用命令打印该对象时才调用的。当然加断点查看时也可以看到debugDescription的描述。
如果你在description不想将一些内容输出的话,你就可以将那些数据写在debugDescription中,让程序员自己调试时可以方便的看到这些数据,而description方法就输出你想要让用户看到的信息就行了。
要点:
这里的不可变对象不是使用系统提供的不可变类,而是在设置属性的时候添加上readnoly
属性
默认情况下的属性是既可读又可写的,这样的类我们在这里成为可变类,但一般的数据未必需要改变。
例如:
@property (nonatomic, copy, readonly) NSString *identifier; @property (nonatomic, copy, readonly) NSString *title; @property (nonatomic, assign, readonly) float latitude;
现在,这个属性就只能用在实现代码内部设置这些属性了,但其实,在对象外部还可以通过“键值编码”技术来设置这些属性,就像“setValue:forKey:”方法。“点语法”也可以,因为点语法就是调用set方法的。这样做虽说可以改动,但是却违背了本心,还会导致数据不同而出现问题,所以不建议更改。
[pointOfInterest setValue:@"abc" forKey:@"identifier"];
这样子可以改动属性值,因为KVC会在类里查找“setIdentifier:”方法,并借此修改此属性。即使没有于公共接口中公布此方法,它也依然包含在类中。不过,这样做等于违规地绕过了本类所提供的API,要是开发者使用这种“杂技代码”的话,那么得自己开应对可能出现的问题。
还有一种可以修改数据的方法就是直接用类型信息查询功能查出属性所对应的实例变量在内存布局中的偏移量,以此来人为设置这个实例变量的值。这样做比绕过本类的公共API还要不合规范。所以不应该因为这个原因而忽视所提的建议,大家还是要尽量编写不可变的对象。
要点:
给方法命名时的注意事项可总结成下面几条规则:
类与协议的命名:
应该为类与协议的名称加上前缀,以避免命名空间冲突,而且应该像给方法起名时那样把词句组织好,使其从左至右读起来较为通顺。基本命名规则就是:命名方式应该一致,如果要从其他的类中继承子类,那么就要遵守其原本的命名惯例。 例如:UIView它的子类就应该是***View,表明其来历。
要点:
要点:
当前很多编程语言都有“异常”(exception)机制,OC也不例外。
首先要注意的是,“自动引用计数”(Automatic Reference Counting, ARC)在默认情况下不是“异常安全的”(exception safe)。具体来说,这意味着:如果抛出异常,那么本应在作用域末尾释放的对象现在不会自动释放了。如果想生成“异常安全”的代码,可以通过设置编译器的标志来实现,不过这将引入一些额外代码,在不抛出异常时,也照样要执行这部分代码。需要打开的编译器标志叫做-fobjc-arc-exceptions。可是在释放资源之前如果抛出异常了,那么该资源就不会被释放了:
id someResource = /*...*/;
if (/*check for error*/) {
@throw [NSException exceptionWithName:@"ExceptionName" reason:@"There was an error" userInfo:nil];
}
[someResource doSomething];
[someResource release];
在抛出异常前先释放someResource,这样做当然能解决此问题,不过要是待释放的资源有很多,而且代码的执行路径更为复杂的话,那么释放资源的代码就容易写的很乱。此外,代码中加入了新的资源之后,开发者经常会忘记在抛出异常前先把它释放掉。
OC语言现在采用的方法是:只在极其罕见的情况下抛出异常,异常抛出之后,无须考虑恢复问题,而且应用程序此时也应该退出。 也就是说,不用再编写复杂的“异常安全”的代码了。
异常只用来处理严重错误(fatal error,致命错误);对于“不那么严重的错误”(nonfatal error,非致命错误),OC语言所采用的编程范式为:令方法返回nil/0,或是使用NSError,以表明其中有错误发生。如:
- (id)initWithValue:(id)value {
if (self = [super init]) {
if (/*Value means instance can't be created*/) {
self = nil;
} else {
//Initialize instance
}
}
return self;
}
这种情况下,如果if语句发现无法用传入的参数值来初始化当前实例,那么就把self设置成nil,这样的话,整个方法的返回值也就是nil了。调用者发现初始化方法并没有2把实例创建好,于是便可以知道其中发生了错误。
NSError的用法更加灵活,因为经由此对象,我们可以把导致错误的原因回报给调用者。NSError对象里封装了三条消息:
要点
我们经常会使用copy函数,但是若是你自定义的类,他自己就不会实现这个函数,此时就需要你自己来实现了,要实现copy函数就的实现NSCopying协议,该协议只有一个方法:
- (id)copyWithZone:(NSZone *)zone;
copy方法由NSObject实现,该方法只是以“默认区”为参数来调用“copyWithZone:”。所以要实现copy函数,他才是关键。
想要重写copy函数,要声明该类遵从NSCopying协议,并实现其中的方法。
#import
@interface EOCPerson : NSObject <NSCopying>
@property (nonatomic, copy, readonly) NSString *firstName; @property (nonatomic, copy, readonly) NSString *lastName;
- (id) initwithFirstName: (NSString*)firstName andLastName: (NSString*) lastName;
@end
实现协议中规定的方法:
- (id) copyWithZone: (NSZone*)zone {
EOCPerson *copy = [[[self class] allocWithZone: zone] initwithFirstName: firstName andLastName: lastName];
return copy;
}
要点: