使用ReactiveCocoa过应该见过很多这样的代码,例:
textField.reactive.textValues
很多的系统类扩展了很多方法,且前面都有一个reactive
的前缀效果。
这是怎么做到的呢?
分析
首先,看一个例子:
var string = "123"
print(string.mj.numbercount())
实例对象后面是.
调用,所以肯定是为系统类扩展了一个属性mj
,并且mj
后面又调用了方法numbercount()
;
所以是为String
类扩展了一个mj
实例对象属性,假定是MJ
类型, 且MJ
存在一个numbercount()
方法
struct MJ {
func numberCount() -> Int {
return 0
}
}
extension String {
var mj: MJ { MJ() }
}
print("123".mj.numberCount())
是不是已经出来了前缀的效果?但这只是开始...
基础版实现
我们要打印字符串个数,要改下numberCount()
里的实现代码
struct MJ {
func numberCount() -> Int {
var count = 0
for c in self where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
但这里会报错
image.png
因为这里要遍历的是字符串,但我们怎么拿到这个要遍历的字符串呢?
需要改造下MJ
这个类,增加一个string
属性,并增加一个构造方法init(_ string: String)
,改造后如下
struct MJ {
var string: String
init(_ string: String) {
self.string = string
}
func numberCount() -> Int {
var count = 0
for c in string where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
然后再改变String
的mj
属性, 在初始化mj
时将self
传进去
extension String {
var mj: MJ { MJ(self) }
}
完整代码如下:
struct MJ {
var string: String
init(_ string: String) {
self.string = string
}
func numberCount() -> Int {
var count = 0
for c in string where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
extension String {
var mj: MJ { MJ(self) }
}
print("123".mj.numberCount())
进阶版实现
假如不只给String
增加mj
前缀呢?还想给其它类,例如给Person
增加mj
前缀,也增加一个run()
方法,怎么做呢?
首先给Person
扩展一个mj
属性
class Person {}
extension Person {
var mj: MJ { MJ(self) }
}
但MJ
类要增加一个Person
属性吗?
struct MJ {
var string: String
var person: Person
init(_ string: String) {
self.string = string
}
func numberCount() -> Int {
var count = 0
for c in string where ("0"..."9").contains(c) {
count += 1
}
return count
}
func run() {
print("run")
}
}
那要很多类增加前缀,岂不是要在MJ
类增加很多属性?
当然不是这样了。并且怎么分离关于String
的numberCount()
和Person
的run()
的方法呢?
配角登场 - 泛型
对MJ
进行泛型改造
struct MJ<Base> {
var base: Base
init(_ base: Base) {
self.base = base
}
}
String
和Person
改造
extension String {
var mj: MJ<String> { MJ(self) }
}
extension Person {
var mj: MJ<Person> { MJ(self) }
}
然后把MJ
的方法,该放到MJ
扩展里
extension MJ where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
extension MJ where Base: Person {
func run() {
print("run")
}
}
上面的方法都是实例方法如果,要怎么类方法怎么做呢?
那么就要增加一个static
类型的mj
,同时在MJ
的扩展里增加static
方法
extension String {
var mj: MJ<String> { MJ(self) }
static var mj: MJ<String>.Type { MJ.self }
}
extension MJ where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
static func test() {
print("test")
}
}
完整代码如下:
class Person {}
struct MJ<Base> {
var base: Base
init(_ base: Base) {
self.base = base
}
}
extension String {
var mj: MJ<String> { MJ(self) }
static var mj: MJ<String>.Type { MJ.self }
}
extension Person {
var mj: MJ<Person> { MJ(self) }
static var mj: MJ<String>.Type { MJ.self }
}
extension MJ where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
static func test() {
print("test")
}
}
extension MJ where Base: Person {
func run() {
print("run")
}
}
高级版实现
在上面的实现过程中还有存在重复的代码,如下;
这两个类的mj
属性是重复的,以后要增加其他类的话,还要复制?粘贴?
extension String {
var mj: MJ<String> { MJ(self) }
static var mj: MJ<String>.Type { MJ.self }
}
extension Person {
var mj: MJ<Person> { MJ(self) }
static var mj: MJ<String>.Type { MJ.self }
}
这时就要用协议
抽取这个公共部分, 让其他类遵循该协议
protocol MJCompatible {}
extension MJCompatible {
var mj: MJ<Self> { MJ(self) }
static var mj: MJ<Self>.Type { MJ.self }
}
extension String: MJCompatible {}
extension Person: MJCompatible {}
注:这里还有个细节需要注意
假如在扩展里增加一个mutating
方法
extension MJ where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
static func test() {
print("test")
}
mutating func run() {
}
}
调用试试
发现报错,因为
mutating
意思是要修改MJ
结构体里的东西,但mj
是实例是作为String
的只读的计算属性,所以要在协议MJCompatible
的属性作如下更改,将只读属性改为可读,可写,但set
什么也不做
extension MJCompatible {
var mj: MJ<Self> {
set {}
get { MJ(self) }
}
static var mj: MJ<Self>.Type {
set {}
get { MJ.self }
}
}
完整终极代码如下:
class Person {}
struct MJ<Base> {
var base: Base
init(_ base: Base) {
self.base = base
}
}
protocol MJCompatible {}
extension MJCompatible {
var mj: MJ<Self> {
set {}
get { MJ(self) }
}
static var mj: MJ<Self>.Type {
set {}
get { MJ.self }
}
}
extension String: MJCompatible {}
extension Person: MJCompatible {}
extension MJ where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
static func test() {
print("test")
}
mutating func run() {
}
}
extension MJ where Base: Person {
func run() {
print("run")
}
}
网友评论