很多刚入门的iOS开发者经过短期训练,都可以熟练的调用API,这时候,写一个tableView,实现一个小动画,独立完成一个交互的功能,已经不在话下。但同时,iOS开发者也会遇到技术上的第一个瓶颈——即拥有独立开发一个功能的水平,却似乎并未达到独立开发一个App的水平;看似什么都会做,什么都能做,却总是不能在第一时间想到最佳方案;功能是完成了,然而效率并不高,代码逻辑在日后也可能需要返工重构。
我认为,突破这个瓶颈的捷径就是掌握设计模式。设计模式是前人总结的。面对开发中常见问题的解决方案——它们行之有效,便于理解,适合举一反三。简单点儿说,设计模式就是程序开发的套路和模板。熟练掌握设计模式,可以提高开发效率,节省开发时间。这样,我们就可以站在前人的肩膀上,去研究、解决那些具有挑战性和未曾解决过的问题。
一、平常开发中用到的设计模式
iOS开发中的设计模式有很多,一般常见的有以下7种。
- MVC:它是应用的一种基本架构,主要目的是将不同的代码归并为不同的模块,做到低耦合、代码分配合理、易于扩展维护。
- 装饰模式(Decorator):它可以在不修改原代码的基础上进行扩展。注意它与继承最大的区别是:继承时,子类可以修改父类的行为,而装饰模式不希望如此。
- 适配器模式(Adapter):它将一个类的接口转换为另一个类的接口,使得原本互不兼容的类可以通过接口一起工作。
- 外观模式(Facade):它用一个公共接口来连接多个类或其他数据类型。公共接口让多个互相之间保持独立,解耦性能良好。同时,使用接口时,外部无须理解其背后复杂的逻辑。另外,就算接口背后的逻辑改变也不影响接口的使用。
- 单例模式(Singleton):此模式保证对于一个特有的类,只有一个公共的实例存在。它一般与懒加载一起出现,只有被需要时才会创建。单例模式的例子有UserDefaults.standard,UIApplication.shared和UIScreen.main。
- 观察者模式(Observer):它定义对象之间的一种一对多的依赖关系,每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。在iOS中的典型实现是NotificationCenter和KVO.
- 备忘录模式(Memento):它在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将改对象恢复到保存之前的状态。
可以把上面7种模式归为一下3类。 - 创建型(Creational):单例模式(Singleton).
- 结构型(Structural):MVC、装饰模式(Decorator)、适配器模式(Adapter)和外观模式(Facade)。
- 行为型(Bahavioral):观察者模式(Observer)、备忘录模式(Memento)
二、什么是MVC?
MVC,即Model-View-Controller。它是苹果公司官方推荐的App开发架构,也是一般开发者最先遇到,最经典的架构。它把整个App分为三个部分:Model负责处理数据;View负责处理UI;Controller是View和Model的桥梁,它将数据从Model层传送到View层并展示出来,同时将View层的交互传到Model层以改变数据。相比传统的MVC,苹果的MVC的特点是,Model层和View层是相互独立的。
由于Controller承担的任务相对较重,在实际开发中,很多初级开发者直接将View和Controller部分的代码全部塞到了ViewController类中,造成了它们的高度耦合。如何解耦View和Controller,在iOS开发中使一个热门的话题。
三、OC和Swift在单例模式的创建上有什么区别?
单例模式在创建过程中,要保证实例变量只被创建一次。在整个开发中需要特别注意线程安全,即使在多线程情况下,依然只初始化一次变量。
在OC中使用GCD来保证这一点的。示例代码如下:
+ (instanceType)sharedManager {
static Manager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareManager = [[Manager alloc] init];
});
return sharedManager;
}
在Swift中,let关键词已经保证了实例变量不会被修改,所以单例的创建就简单很多:
class Manager {
static let shared = Manager()
private init() {}
}
四、什么是装饰模式
装饰模式是在不改变原封装的前提下,为对象动态添加新功能的模式。在OC中,它的实现模式为Category和Delegation;在Swift中,它的实现形式为Extension和Delegation。
- Category的好处之一是可以给类增加新的方法,它也可以利用动态特性增加新的变量。同时,Category的出现也减轻了类的负担,可以利用它将代码分散开。它的文件名形式一般为“类名+扩展名”。
- Extension在Swift中的地位等同于Category在OC中的地位。它更强大的地方在于可以为Protocol扩展完成默认实现。
- Delegation是程序中一个对象代表另一个对象,或者一个对象与另一个对象协同工作的模式,其一般配合protocol使用。例如tableView的UITableViewDataSource和UITableViewDelegate就是典型的Delegation模式。
注意,delegate一般被声明为weak,以防止循环引用。
五、什么是观察者模式(Observer)
观察者模式定义对象之间的一种一对多依赖关系,每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。在iOS开发中,典型的推模型实现方式为通知和KVO.
1.通知(Notifications)
- 注册观察者Observer:通过NotificationCenter的addObserver:selector:name:object接口来注册对某一类型通知感兴趣。在注册时一定要注意,NotificationCenter不会对观察者进行引用计数+1的操作。
- 通知中心NSNotificationCenter:通知的枢纽。
- 被观察的对象:通过postNotificationName:object:userInfo:发送某一类型通知,广播改变。
- 通知对象Notification:当有通知来的时候,通知中心会调用观察者注册的接口来广播通知,同时传递存储着要更改内容的Notification对象。
2.KVO
KVO,Key-Value Observer,即键值观察。它是一种没有通知中心的观察者模式的实现方式。一个主体对象管理所有依赖于它的观察者对象,并且在自身状态发生改变时主动通知观察者对象。KVO是一个纯OC的概念,Swift当前没有很好的动态机制,而且目前只有NSObject才支持KVO.它的具体实现步骤如下。
(1)注册观察者
(2)更改主题对象属性的值,即触发发送更改的通知。
(3)在制定的回调函数中,处理收到的更改通知。
在Swift 4中,我们不需要再手动的回收observer了。同时配合NSKeyValueObservation,我们可以更简单地使用KVO了,下面是示例代码:
// 在Swift4中,NSObject的类不再自动推断为@objc,需要用@objcMembers来声明其Objective-C特性
@objcMembers class User : NSObject {
// dynamic关键词对observe的闭包来说是必需的
dynamic var email : String
init(email: String) {
self.email = email
}
}
let user = User(email:"user@hotmail.com")
// 注册观察email属性值,闭包中为若发生变化做出的相应处理
let observation = user.observe(\.email) {(user,change) in
print("User's new email:\(user.email)")
}
user.email = "user@outlook.com"
六、什么是备忘录模式(Memento)
备忘录模式是一种保存对象当前的状态,并在日后可以恢复的模式。注意它不会破坏对象的封装,也就是说,私有对象也能保存下来。
备忘录模式最经典的使用方法就是用UserDefaults来读写,同时配合栈可以存储一系列状态。它经常用于初始化、重启、App前后台状态改变等场景。