OC没有其他语言所内置的那种命名空间的机制,所以我们命名的时候要设法避免潜在的一个命名冲突,在某些情况下如果在运行期载入了含重名的程序库,可能会让整个应用程序崩溃。
这里的内容比较少,其实就是我们采用三个字符前缀,apple采用两个字符前缀
所有对象都要初始化,初始化时候有着不同的方法,创建类的实例方法不止一种,比方说NSDate这个类
这里有一个initWithTimeIntervalSinceReferenceDate:是一个全能初始化方法,我们设置一个类别来理解这个全能初始化方法的概念:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EOCRectangle : NSObject
@property (nonatomic, assign) float width;
@property (nonatomic, assign) float height;
- (id)initWithWidth:(float)width Height:(float)height;
@end
#import "EOCRectangle.h"
@implementation EOCRectangle
- (id)initWithWidth:(float)width Height:(float)height {
if (self = [super init]) {
_width = width;
_height = height;
}
return self;
}
- (instancetype)init {
return [self initWithWidth:6 Height:5];
}
@end
我们这里采用自定义的方法覆盖了init方法。
这里就可以保证我们调用任意一种init方法的时候不会出现一个报错,倘如我们又出现了一个新的一个子类,就像正方形这个类,
#import "EOCRectangle.h"
NS_ASSUME_NONNULL_BEGIN
@interface EOCSquare : EOCRectangle
- (instancetype)initWithDimension:(float)dimension;
@end
NS_ASSUME_NONNULL_END
@implementation EOCSquare
- (instancetype)initWithDimension:(float)dimension {
return [super initWithWidth:dimension Height:dimension];
}
@end
这里他的子类也要使用这个全能初始化方法,但是这里会出现一个问题,也就是我们这里的代码,如果调用他父类的方法就会出现一个问题,创建出了一个长和宽不同的正方形,所以我们需要重写父类的一个方法。
- (id)initWithWidth:(float)width Height:(float)height {
return [self initWithDimension:MAX(width, height)];
}
有些时候可能要实现多个全能初始化方法,比方说这里的一个
NSCoding这个协议
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
_width = [coder decodeFloatForKey:@"width"];
_height = [coder decodeFloatForKey:@"height"];
}
return self;
}
如果子类要实现的话,我们要先调用父类的超类对应的方法,逐层向上,实现initWithCoder也是这样
我们如果想直接打印对象的话如果不重写description方法的话会看到下面的内容:
<Person: 0x60000022b180>
我们如果想要输出更有用的信息也很简单,只用重写description方法就好了
- (NSString *)description
{
return [NSString stringWithFormat:@"%@: %p, \%@ %@\"", [self class], self, _firstName, _name];
}
//输出结果
Person: 0x60000023a5e0, 123 nan"
Type: Notice | Timestamp: 2025-01-21 15:56:34.193344+08:00 | Process: fragment | Library: fragment | TID: 0x21153bf
其实我们还可以借助NSDictionary来更方便我们看到对象内部的一个属性
- (NSString *)description
{
return [NSString stringWithFormat:@"%@: %p, %@", [self class], self, @{@"firstName":_firstName, @"name":_name}];
}
//输出结果
Person: 0x6000002043c0, {
firstName = 123;
name = nan;
}
这里还有一个\- (NSString *)debugDescription
这个方法其实是在我们的一个调试控制台里面输入命令来让他打印的。
在具体编程实践的时候,尽量使用不可变对象,也就是我们尽量吧对外公开的属性设为只读,
@interface Person : NSObject
@property (nonatomic, copy, readonly) NSString* name;
@property (nonatomic, copy, readonly) NSString* firstName;
-(id) initWithName:(NSString*)firstName lastName:(NSString*)name;
@end
在有些时候我们想重新修改封装在对象的数据,但是却不想数据在外部被别人修改,那么可以在extension中重新声明这个属性。
@interface Person ()
@property (nonatomic, copy, readwrite) NSString* firstName;
@end
当我们返回一个对象的要求是collection的时候,我们还是尽量返回一个不可变的对象,假设他可以进行一个添加和删除,但是我们要设置成一个不可变的collection,这个不可变collection是内部可变的一个collection的复制
@interface Person : NSObject
@property (nonatomic, copy, readonly) NSString* name;
@property (nonatomic, copy, readonly) NSString* firstName;
@property (nonatomic, copy, readonly) NSSet* firend;
-(id) initWithName:(NSString*)firstName lastName:(NSString*)name;
@end
@implementation Person {
NSMutableSet* internalSet;
}
- (NSSet *)firend {
return [internalSet copy];
}
-(void)addFirend:(Person*)person {
[internalSet addObject:person];
}
这样可以避免外部直接修改set里面的内容。
这里其实就是在强调我们命名要清晰协调。
要点
使用什么前缀和个人喜好而定,其中最好包括下划线和字母p作为前缀。
比方说:
- (void)p_privateMethod;
不要用单个下划线作为私有方法的前缀,这种方式会和苹果公司冲突
OC只在极罕见的请况下才抛出异常
异常只用于极其严重的问题,也就是NSException的使用场景并没有那么多,而是NSError比较多,这里我们先认识一下NSError
封装了三个信息:
错误发生的范围
Error code (错误吗,整数)
独有的错误代码,用以指明某个范围内具体发生哪些错误
User info (用户类型, 字典)
有关此错误的额外信息,其中包含一段本地化的描述
常见的是通过error在委托协议中传递此错误,我们在网络协议中经常看到这种内容
我们可以创建属于自己的程序库中所发生的错误指定一个专用的错误范围的字符串
为什么会出现zone,zone是不同的区
我们要重写copyWithZone:这个方法
这里先看一个例子:
-(id)copyWithZone:(NSZone *)zone {
return [[[self class] allocWithZone:zone] initWithName:_firstName lastName:_name];
}
这个实现主要是创建一个新的对象,然后进行一个全能初始化的操作,让他执行所有的初始化工作。
但在某些情况中我们会遇到一些容器类的数据结构,我们有些时候要采用浅拷贝,有些时候采用深拷贝。
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
如果copyItems参数为yes,这个方法会给数组中的每个对象发生copy,这个方法才是容器的一个深拷贝,而采用普通的copy方式只是一个浅拷贝,不会拷贝每一个set中的元素