Effective Obj-C <二>

API构建

这个领域可讲的点很多,设计规范,设计模式,设计范式,架构模式等。主要的宗旨就是可扩展,可维护,最终达到open-close原则。

命名空间

没有命名空间是我心中永远的痛啊,不过通过规范命名也能尽量避免,而更糟糕的是如果动态添加时碰到重名,崩溃就在一念间。

还有亮点很容易被忽视,就是C语言函数总是作为顶级符号,不管你是否是声明在.h中。还有一个是如果你的库引用了第三方的记得在第三方已有的前缀之前在加上你自己的前缀。

designed initializer和description方法

这个东西不是很受重视,不过现在苹果推出了一个宏,专门用来指定NS_DESIGNATED_INITIALIZER,暂且叫它指定初始化方法吧。

这个东西很有用,尤其是你有多个初始化方法时,不会引起子父类方法调用混淆。有一个注意点就是,父类的指定初始化最好在子类中复写一遍,以防止参数出错。

description这个方法也不常用,不过在调试的过程中却非常有用,尤其是你写的框架给其他人使用的时候。

尽量用不可变

不论是容器类型还是类属性的读写权限,对外暴露的尽量用不可变,只读的。可以通过方法写入数据,而对外暴露的则是readonly属性,然后在分类中改为readwrite属性。

命名规范

主要分方法命名还有类协议等命名,主要原则就是,命名方式要协调一致,可以多看苹果提供的类暴露出来的命名方式。

私有方法加前缀等手段都应该遵循,常量的定义,都应该区分是私有的还是公共的。

Error&&Exception

现在是ARC的年代,实在不推荐用异常,很容易引起内存泄露,当出现严重的问题时才会采用抛出异常的方式。在Obj-C中用error或者nil来代替异常,可以通过委托返回error(异步)或者指针返回(同步)。

copy

主要要弄清楚可变对象不可变对象的copy,mutableCopy,还有深拷贝和浅拷贝。

深拷贝不仅要拷贝对象自身,还要拷贝底层的数据结构,系统实现的容器类的copy都是浅拷贝。

不可变对象的copy总是返回自己。copy总是应该返回不可变对象,可变对象总应该用mutableCopy来获取。

Obj-C特有的协议和分类

这两个特性是非常强大的,也是为数不多的语言亮点,尤其是分类,大多数面向对象语言都有类似协议的替代品,然而并没有很多具有分类的特点。

灵活的通信机制

在Obj-C中有一个特色,就是delegate和datasource,这种模式灰常有用,可以用来解除试图和数据的耦合度,主要通过协议来完成。一般情况下,datasource是数据流入,delegate是数据流出。

注意点:

  • 这个模式不叫代理模式,不叫代理模式,不叫代理模式,重要的事说三遍,真实名字叫委托模式。
  • 还有委托对象的引用应该是weak,否则很容易就内存泄露了。
  • 一定,务必要判断委托对象是否实现了协议。

Category

这个东西有点被滥用了,是个好东西,但不应该用来代替继承和组合,一个用处可以重新组织类结构,还有一个可以用来玩一些黑魔法。

@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>

@property (readonly) NSUInteger count;
- (id)objectAtIndex:(NSUInteger)index;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithObjects:(const id [])objects count:(NSUInteger)cnt NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

@end
@interface NSArray (NSArrayCreation)

+ (instancetype)array;
+ (instancetype)arrayWithObject:(id)anObject;
+ (instancetype)arrayWithObjects:(const id [])objects count:(NSUInteger)cnt;
+ (instancetype)arrayWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
+ (instancetype)arrayWithArray:(NSArray *)array;

- (instancetype)initWithObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
- (instancetype)initWithArray:(NSArray *)array;
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;

+ (NSArray *)arrayWithContentsOfFile:(NSString *)path;
+ (NSArray *)arrayWithContentsOfURL:(NSURL *)url;
- (NSArray *)initWithContentsOfFile:(NSString *)path;
- (NSArray *)initWithContentsOfURL:(NSURL *)url;

@end

注意点:

  • 总是为第三方的类的分类加前缀
  • 总是为分类中的方法名加前缀
  • 不建议在分类中声明属性

Extention和匿名对象

这个特性倒是一个好东西,以前想隐藏数据只能用实例变量放在.m中,有了类扩展后,可以在类内部声明属性,而不对外公布。当然由于Obj-C是一门非常动态的语言,这也不能做到真正的私有数据。

#import "ViewController.h"

@interface ViewController ()<UIWebViewDelegate, UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *wv;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}
@end

还有一个用处就是和C++混编的时候可以不暴露在头文件中,然后可以控制属性的访问权限。

通过协议,我们可以实现匿名对象,可以影藏细节,而不用暴露具体的数据结构。

@interface ViewController : UIViewController

- (id<NSCopying>)object;

@end