《编写高质量OC代码》已顺利完成一二三四五六七八篇!
附上连接:
iOS 编写高质量Objective-C代码(一)—— 简介
iOS 编写高质量Objective-C代码(二)—— 面向对象
iOS 编写高质量Objective-C代码(三)—— 接口和API设计
iOS 编写高质量Objective-C代码(四)—— 协议与分类
iOS 编写高质量Objective-C代码(五)—— 内存管理机制
iOS 编写高质量Objective-C代码(六)—— block专栏
iOS 编写高质量Objective-C代码(七)—— GCD专栏
iOS 编写高质量Objective-C代码(八)—— 系统框架html
这一篇,将经过介绍OC的接口和API设计
来提升Objective-C的代码质量编程
OC里没有命名空间的概念(
namespace
)。因而,咱们须要给类加前缀,避免重名,避免发生命名冲突。固然,不只是类名,一些全局变量和方法也须要加上适当的前缀加以区分。安全
因此,咱们要:bash
选择与公司、工程相关的前缀做为类名的前缀。框架
为了不重复引用第三方库带来的冲突,必要时也要为他们加上前缀区分。编程语言
全能初始化方法
,并在文档中写明注释。其余的初始化方法全调用此全能初始化方法。举个例子来讲:能够看一下NSDate类中定义了一个全能初始化方法:ide
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
复制代码
其他的初始化方法 定义在NSDate (NSDateCreation) 分类中测试
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
复制代码
在官方文档中,关于NSDate有以下说明 If you want to subclass NSDate to obtain behavior different than that provided by the private or public subclasses, you must:
atom
initWithTimeIntervalSinceReferenceDate:
, one of the designated initializer methods解释:选定一个方法做为全能初始化方法,剩下的其他的初始化方法都调用这这个方法初始化,这样作的好处是之后若是初始化的逻辑更改了只需更改全能初始化方法,或者即便子类覆写的时候也只覆写全能初始化方法~spa
本条写的是经过覆写
description
(或者debugDescription
)方法来在NSLog打印
(或者LLDB打印
时)输出更多的自定义信息。
下面举个例子:
- (NSString *)description {
return [NSString stringWithFormat:@"<%@: %p, %@>",
[self class],
self,
@{
@"qi": _qi,
@"share" : _share}
];
}
复制代码
readonly
修饰~(默认是readwrite修饰)~。这样外部只能读取数据而不能修改数据,保证了这个类的实例所持有的数据更加安全。~尤为是不要把可变的collection做为属性公开,而是应该提供相应的方法修改可变的collection。~接口方法
修改KVC(Key-Value Coding)
技术 ~这种技术容许对象的数据或属性能够在运行时经过其键名进行查找,其中,属性的名称即为其值的键名。在静态语言中,这样的作法是不可能的。KVC大大的增长了设计的自由度:经过KVC,无需知道对象的类型便可访问其属性或数据。~例如: 不推荐写法:
//Animals.h
@property (nonatomic, strong) NSSet *animals;
复制代码
应改成:
//Animals.h
@interface Animals : NSObject
@property (nonatomic, strong, readonly) NSSet *animals;
- (void)addAnimal:(NSString *)animal;
- (void)removeAnimal:(NSString *)animal;
@end
//Animals.m
@implementation Animals {
NSMutableSet *_mutableAnimals;
}
- (NSSet *)animals {
return [_mutableAnimals copy];
}
- (void)addAnimal:(NSString *)animal {
[_mutableAnimals addObject:animal];
}
- (void)removeAnimal:(NSString *)animal {
[_mutableAnimals removeObject:animal];
}
复制代码
可是,小编认为这样写当然有好处:保证了数据的安全性,但代码量也会提高很多。因此推荐你们能够有选择的使用,对一些重要的类才有使用必要。
另外,若是某属性仅能够在对象内部修改,则能够在.h
文件中声明为readonly
。而后 在.m的类扩展
中将属性扩展为readwrite
属性。
固然,师父说了:也能够在.h
文件中把属性声明为readonly
,在.m
文件中经过实例变量
修改值,当block内部修改值时,能够用self->实例变量
的方法访问修改。~(小编测试过,确实有效。欢迎路过的大神继续讨论)~
师父语录:“写OC代码像是在讲故事,而读OC代码更像是在听故事。”
这句话要归功于OC清晰而协调的命名方式。
例如:咱们想给初始化一个矩形,并给他的宽和高赋值。
// C++:
Rectangle *aRectangle = new Rectangle(5.0, 10.0);
// Objective-C:
Rectangle *aRectangle = [[Rectangle alloc] initWithWidth:5.0 andHeight:10.0];
复制代码
很显然,OC的方法能够很直接的看出所要传递的参数的具体含义,而C++的传参就并无这么直观。
这一条:给你们参考一下咱们QiShare团队制定的 iOS 代码规范 QiShare更喜欢经过#pragma mark -
来区分 公私有等方法
。 例如:
#pragma mark - Private Functions
// code...
#pragma mark - Action functions
// code...
#pragma mark - Request functions
// code...
#pragma mark - xxxDataSource
// code...
#pragma mark - xxxDelegate
// code...
复制代码
固然,你们也能够根据团队本身定制规范。
不少语言都有异常处理机制,Objective-C也不例外。@throw
可是, 注意:OC里抛异常可能会致使内存泄漏 注意:OC里抛异常可能会致使内存泄漏 注意:OC里抛异常可能会致使内存泄漏 解释:OC里的ARC机制(Automatic Reference Counting)在默认状况下是“无异常安全”。简单来讲,一旦抛出异常,对象就没法正常自动释放了。 因此,
nil
或者0
(例如:初始化的参数不合法,方法返回nil或0)NSError
在iOS开发中,使用对象时常常须要拷贝它,这时咱们会经过copy/mutableCopy
方法完成。 若是咱们想让本身的类支持拷贝,那就必需要实现NSCopying
协议,该协议只有一个方法:
- (id)copyWithZone:(nullable NSZone *)zone;
复制代码
固然,若是要求返回对象是可变的那就要实现NSMutableCopying
协议,对应方法:
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
复制代码
同时,在拷贝对象时,要注意是执行浅拷贝仍是深拷贝
那么引出了一个概念:什么是深拷贝?什么是浅拷贝?
指针
又拷贝出新的Object
)指针
指向原来的Object
)这里有张很经典的图解:
[NSMutableArray copy]
拷贝出 =>
NSArray
(不可变)
[NSArray mutableCopy]
拷贝出 =>
NSMutableArray
(可变) 这种操做能够在可变版本和不可变版本间切换。
说太多,不如给一个Demo~
QiShareMember.h:
@interface QiShareMember : NSObject <NSCopying>
@property (nonatomic, copy, readonly) NSString *name; //!< 姓名
@property (nonatomic, copy, readonly) NSString *sex; //!< 性别
@property (nonatomic, assign, readonly) NSUInteger age; //!< 年龄
//! 初始化方法
- (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age;
- (void)addFriend:(QiShareMember *)friend;
- (void)removeFriend:(QiShareMember *)friend;
@end
复制代码
QiShareMember.m:
@implementation QiShareMember {
NSMutableSet *_friends;
}
- (instancetype)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSUInteger)age {
if (self = [super init]) {
_name = [name copy];
_sex = [sex copy];
_age = age;
_friends = [NSMutableSet new];
}
return self;
}
- (void)addFriend:(QiShareMember *)friend {
[_friends addObject:friend];
}
- (void)removeFriend:(QiShareMember *)friend {
[_friends removeObject:friend];
}
- (id)copyWithZone:(NSZone *)zone {
QiShareMember *copy = [[[self class] allocWithZone:zone] initWithName:_name andSex:_sex andAge:_age];
copy->_friends = [_friends mutableCopy]; //!< 注意friends只是一个实例变量不是一个属性,因此不能用点语法
return copy;
}
@end
复制代码
最后,特别致谢《Effective Objective-C 2.0》第三章