内容简介:最近在写新项目的时候,在一个下拉组件中看到了 .es 的语法,于是想到了很多的第三库如 Kingfisher, RxSwift 都使用了类似的 .kf, .rx 语法,从而引发了一段思考。首先说说这个语法出现的场景,通常自己写的类或者封装的组件都会自己加前缀,但是在系统方法扩展的时候官方在 OC 时代给到的推荐是因为是 RxSwift 的使用者,很早的时候记得当时的语法糖是
最近在写新项目的时候,在一个下拉组件中看到了 .es 的语法,于是想到了很多的第三库如 Kingfisher, RxSwift 都使用了类似的 .kf, .rx 语法,从而引发了一段思考。
首先说说这个语法出现的场景,通常自己写的类或者封装的组件都会自己加前缀,但是在系统方法扩展的时候官方在 OC 时代给到的推荐是 前缀_ 的方式,在 Swift 语言出现后一段时间,开发者保留了这样的写法,但是因为 Swift 语言的发展,以及语言的特性,面向协议编程 (Protocol Oriented Programming,以下简称 POP),从而引出下面的写法。
.rx
因为是 RxSwift 的使用者,很早的时候记得当时的语法糖是 rx_
, 这个是非常重度的 OC 语法推荐写法,在自己对系统方法进行扩展的时候需要加上前缀,方式方法名称重复。但是我们发现后续版本中这个语法糖做出了改变,大家可以参考 RxSwift 的这个帖子
[RxCocoa] Move from rx_
prefix to a rx.
proxy (for Swift 3 update ?)
,这篇帖子的标题解释了一个很重要的概念,就是 rx_ 向 rx. 转变的时候是由带前缀的方法名称向协议去转变,通过拥有对象的协议去实现扩展方法。
具体实现
鉴于上面 RxSwift Issue 时间比较久远,这边提供几个近期的完整实现:
实现 Base
实现一个 Base 的 Struct/ Class,推荐 Struct。
这个 Struct 将真实的对象包裹起来,作为一个泛型结构体,不做任何实际操作。
public struct KingfisherWrapper<Base>{ public let base: Base public init(_ base: Base) { self.base = base } }
实现 Protocol 和 .kf 方法
定一个 protocol,不实现任何变量方法声明,防止其他的 protocol 继承会修改到变量方法的实现。
然后实现 protocol 的 extension,提供一个 default implementation,这边其实就是实现了一个 kf 的属性,这个属性是 KingfisherWrapper 的实例, public var kf: KingfisherWrapper<Self>
Self 用在协议里面,代表的是遵守协议的对象(类/结构体/枚举)类型,即 Base 类型,根据 Base 类的不同,实现对应类里面的方法。
这样,就相当于实现了 kf
的命名空间。
public protocol KingfisherCompatible:AnyObject{ } extension KingfisherCompatible{ /// Gets a namespace holder for Kingfisher compatible types. public var kf: KingfisherWrapper<Self> { get { return KingfisherWrapper(self) } set { } } }
将 Protocol 加载到所需的 Base 类并通过Extension + where Base 实现 Base 类的特定代码
将我们需要扩展的系统类遵循 protocol,这样对应的 KingfisherWrapper 对象就可以实现对应的系统类里面的方法。
在实现方法里面有个特别要注意的点,所以 UIImageView 的属性,即可以用 self.
调用的属性都需要变成 base.
,因为这边的调用 .kf
的时候每次返回的都是全新的 KingfisherWrapper 实例对象,并不是调用本身对象。
因为
extension UIImage:KingfisherCompatible{ } extension KingfisherwhereBase:UIImageView{ public func setImage(image: UIImage) { base.image = image } }
使用场景
从上面的分析中来看,这样通过协议命名空间方法实现的 extension 看上去会跟优雅,也能过解决在项目中 manually 方式引入第三方库的时候,出现同名的扩展引起的冲突。但是在扩展的时候我同样发现一个问题,对于 initializers methods
是没办法使用命名空间去扩展的,我们只能对应的给一个 func 返回设置好的 Color 作为返回值。
convenience init(hex: Int, alpha: CGFloat = 1) { var t_alpha = alpha if t_alpha < 0 { t_alpha = 0 } if t_alpha > 1 { t_alpha = 1 } let red = (hex >> 16) & 0xff let green = (hex >> 8) & 0xff let blue = hex & 0xff self.init(red: red, green: green, blue: blue, alpha: t_alpha) }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解Java虚拟机(第2版)
周志明 / 机械工业出版社 / 2013-9-1 / 79.00元
《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》内容简介:第1版两年内印刷近10次,4家网上书店的评论近4?000条,98%以上的评论全部为5星级的好评,是整个Java图书领域公认的经典著作和超级畅销书,繁体版在台湾也十分受欢迎。第2版在第1版的基础上做了很大的改进:根据最新的JDK 1.7对全书内容进行了全面的升级和补充;增加了大量处理各种常见JVM问题的技巧和最佳实践;增加了若干......一起来看看 《深入理解Java虚拟机(第2版)》 这本书的介绍吧!