前言
现在很多公司的iOS新项目都开始用Swift来代替OC开发了,Swift带来的亮点和新功能很多,但我觉得最重要的一点是引导我们编程思想的改变,将我们在OC中用到的传统的面向对象编程思想OOP(object-oriented programming)向面向协议编程思想POP(protocol oriented programming)以及面向值的编程思想VOP(value-oriented programming)上转变,苹果也让我们开发者在编程的时候“从一个protocol开始,别从一个class开始”,再加上Swift的标准库中出现了比重很大的结构体struct和枚举类型enum,所以在Swift中灵活运用各种协议protocol和值类型value来实现功能既实用又优雅。而在Swift编程过程中无处不在的extension,不仅让我们在代码结构上发生了巨大改变,同时在编程理念上也与POP不谋而合。
extension
extension,顾名思义就是扩展,类似于OC中的category,但Swift中的extension功能却强大的多, 可以扩展class struc enum 甚至是protocol,来给他们:提供便利构造方法、增加运算属性、定义实例方法和类方法、定义下标、使已有的类型遵循协议等等。
现在有一个需求,A、B和C三个类,都需要扩展一个属性或者方法用处理同样的功能,OOP的做法:让A、B和C继承于一个基类D,然后给D中加上这个属性,或者当A、B和C三个类不方便继承自一个基类时,各自分别扩展一个属性。不过这样做感觉很死板的样子。
而面向协议编程POP的做法:写声明一个协议someProtocol,协议中声明这个属性,然后让A、B、C三个类都遵循这个someProtocol,分别再实现这个协议中的属性,如下所示:
//定义protocol protocol someProtocol { var clickCount: Int { set get } func ClickEvent(action: String,value: NSNumber) } extension A: someProtocol{ var clickCount: int { set get } func ClickEvent(action: String,value: NSNumber){ //实现代码 } }
限定扩展 extesion...where..
而此时来了新的需求,需要给A类扩展另外一个属性,但B和C暂时不需要,大部分人的思维肯定是既然只有A需要,那我们就单独给A来扩展一个属性不就可以了吗,这样肯定行得通,但既然我们都已经走上了POP编程的道路,而且Swift也鼓励我们尽量用protocol来处理问题,那我们应该怎么在protocol上做文章呢?
首先我们想到了给someProtocol扩展一个属性,但这样不只是A能用,同样遵循了该协议的B和C,以及将来遵循这个协议的所有class、struct等等都扩展得到了这个属性,这从代码的逻辑性上来说是不严谨的,于是苹果在Swift2.0的时候加入了一个新功能,可以在给protocol扩展的时候添加限定,就是说在满足该限定条件(遵循另一个协议或者满足某个类型)下才能允许使用此扩展下的属性或方法, 而这个限定就是通过where来添加的, 先看代码:
extension someProtocol where Self: UIViewController{ var otherProperty: String{ return "something you want" } func handleError(error: String) { //实现代码 }}
以上代码的意思就是:Self代表遵循了someProtocol的某个类,只有这个类是继承于UIViewController时,才可以使用otherProperty这个属性和handleError这种方法,这就是限定扩展的基本写法,当然此处的UIViewController也可以是另外的一个协议anotherProtocol,就是说你只有遵循了anotherProtocol的前提下,你才能使用someProtocol中的扩展内容。
RxSwift/RxCocoa中的限定扩展应用
Swift开发到一定阶段,通常都会引入RxSwift框架来进行响应式编程和敏捷开发,而在代码中与RxSwift同时会引入的另一个库RxCocoa,RxCocoa是Rx对iOS的原生API中UIKit以及Foundation中的视图(UIView)、控制事件(Control Event)、键值观察(KVO)、通知(Notification)等的扩展,以便在开发时更方便的对这些原生组件进行Rx应用。例如:
//nameTextField是一个UITextField控件,可以直接通过.rx.text获取到该控件中输入内容的事件序列,然后再进行处理 let nameObservable = nameTextField.rx.text .shareReplay(1).map { $0!.characters.count > 0 } //registerButton是一个UIButton,通过.rx.tap能获取到该button的点击事件序列 registerButton.rx.tap .bindTo(someObservar) .addDisposableTo(disposeBag)
而以上例子中的rx.text以及rx.tap是怎么实现的呢,咱们来看RxCocoa的源码一窥究竟:
/// Extend NSObject with `rx` proxy.将所有NSObject子类都遵循ReactiveCompatible协议 extension NSObject: ReactiveCompatible { } //ReactiveCompatible这个协议中扩展了rx属性,类型为struct类型的Reactive extension ReactiveCompatible { /// Reactive extensions. public var rx: Reactive { get { return Reactive(self) //此处返回Reactive实体,将self赋给base属性 } set { // this enables using Reactive to "mutate" base object } } }
以UIButton举例,通过以上实现,我们明白了通过UIButton.rx可以获得一个Reactive类型的属性,通过Reactive(self) 将button自身赋给了Reactive这个struct的Base属性,来看源码:
public struct Reactive<Base>{ /// Base object to extend. public let base: Base /// Creates extensions with base object. /// /// - parameter base: Base object. public init(_ base: Base) { self.base = base //上面的return Reactive(self),将self赋给了base } }
通过Reactive的构造方法,此时UIButton.rx获得的这个Reactive实体中的Base类型就是UIButton了,而base就是这个UIButton对象本身,而再接着通过rx.tap又是怎么获得点击事件的呢,这就用到了限定扩展这个非常实用的功能了,接着看源码:
//点击事件扩展,可通过button.rx.tap来观察订阅 extension Reactive where Base: UIButton { /// Reactive wrapper for `TouchUpInside` control event. public var tap: ControlEvent { return controlEvent(.touchUpInside) } }
以上就是RxCocoa中对Reactive进行的限定扩展,只有当Reactive的Base类型是UIButton时,才能使用Reactive下的tap属性,而这个tap属性就是RxSwift封装好的点击事件序列。RxCocoa中也同时对改变UIButton的image和title进行了扩展,如下:
//扩展方法,可通过绑定Observable来动态修改UIButton的title和image extension Reactive where Base: UIButton { /// Reactive wrapper for `setTitle(_:for:)` public func title(for controlState: UIControlState = []) -> UIBindingObserver<Base, String?> { return UIBindingObserver<Base, String?>(UIElement: self.base) { (button, title) -> () in button.setTitle(title, for: controlState) } } /// Reactive wrapper for `setImage(_:for:)` public func image(for controlState: UIControlState = []) -> UIBindingObserver<Base, UIImage?> { return UIBindingObserver<Base, UIImage?>(UIElement: self.base) { (button, image) -> () in button.setImage(image, for: controlState) } } /// Reactive wrapper for `setBackgroundImage(_:for:)` public func backgroundImage(for controlState: UIControlState = []) -> UIBindingObserver<Base, UIImage?> { return UIBindingObserver<Base, UIImage?>(UIElement: self.base) { (button, image) -> () in button.setBackgroundImage(image, for: controlState) } } }
我们自己也可以对UI控件来进行其他的Rx扩展,以满足特定需要,比如说只有当用户名和密码同时满足某个条件时,登录按钮才可以点击以及改变背景颜色,因此我们可以将某种条件转换为一个Bool类型的可观察序列Observabel<Bool> ,然后对UIButton进行一个观察者类型btnValidState的扩展,然后绑定就可以随时进行监控了。
//利用限定扩展来自定义对UIButton的Reactive扩展 可以通过rx来访问 extension Reactive where Base:UIButton{ var btnValidState:UIBindingObserver<Base,Bool>{ return UIBindingObserver(UIElement: base, binding: { (button, valid) in button.isEnabled = valid button.backgroundColor = valid ? mainColor : bgGrayColor2 }) } } //将用户名和密码的String序列转换成Bool序列 let nameObservable = userNameText.rx.text .shareReplay(1).map { $0!.characters.count > 0 } let passObservable = passwordText.rx.text .shareReplay(1).map { $0!.characters.count > 0 } //将以上两个序列合成一个序列,绑定到我们扩展的btnValidState属性上 //可以看到此时可以通过loginBtn.rx.btnValidState获取到,这样保持了RxSwift代码的一致性 _ = Observable.combineLatest(nameObservable,passObservable){$0 && $1}.bind(to: loginBtn.rx.btnValidState).disposed(by: disposeBag)
以上就是限定扩展的基本使用和一些实战中的应用,这在一定程度上确实能改变我们的编码思维和方式,这也是Swift给我们带来的非常灵活的改变。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。
使用扩展可添加现有类,结构或枚举类型的功能。 使用扩展添加类型功能,但扩展无法覆盖功能。 Swift扩展功能 - 添加计算属性和计算类型属性 定义实例和类型方法。 提供新的初始化程序。 定义下标 定义和使用新的嵌套类型 使现有类型符合协议 使用关键字声明扩展名,语法如下 - 类型也可以添加扩展,使其成为协议标准,它的语法类似于类或结构。 计算属性 使用扩展,也可以扩展计算的“实例”和“类型”属性。
扩展就是向一个已有的类、结构体或枚举类型添加新功能。 扩展可以对一个类型添加新的功能,但是不能重写已有的功能。 Swift 中的扩展可以: 添加计算型属性和计算型静态属性 定义实例方法和类型方法 提供新的构造器 定义下标 定义和使用新的嵌套类型 使一个已有类型符合某个协议 语法 扩展声明使用关键字 extension: extension SomeType { // 加到SomeType
我特别想知道: 扩展名驻留在哪里(文件和命名约定)? 什么是扩展语法? 几个简单的常用示例是什么?
问题内容: Swift 1.2支持无序集合,但似乎在Sets上不起作用,因此我决定在操场上变聪明并尝试: 这似乎有效。所以我尝试扩展Set: 而且我认为有一个很好的理由为什么它不起作用,例如这里的示例: 关于如何扩展Set以可靠地使用map(_ :)的任何想法?谢谢大家 问题答案: 更新: Swift 2和3进行了很多更改。的通用占位符现在是,而不是,并且所有集合都有一个返回 数组 的方法 。 对
本文向大家介绍Django 内置权限扩展案例详解,包括了Django 内置权限扩展案例详解的使用技巧和注意事项,需要的朋友参考一下 当Django的内置权限无法满足需求的时候就自己扩展吧~ 背景介绍 overmind项目使用了Django内置的权限系统,Django内置权限系统基于model层做控制,新的model创建后会默认新建三个权限,分别为:add、change、delete,如果给用户或组
使用 Swift 扩展 Weex Swift和Objective-C 混编 参考完整 例子 使用 Swift 进行 module 扩展 因为 module 暴露 method 是通过Objective-C宏来做的,调用的时候是通过反射,所以Swift扩展 module 通过extensionObjective-C的类。 新建 WXSwiftTestModule.h/m 和 WXSwiftTestM