为了避免命名冲突,在需要对系统类型进行扩展时,我们需要在扩展的方法前面添加一个前缀,类似“命名空间”的功能,比如:
"ABC123456".abc.test()
其中,test() 是我们为 String 扩展的方法,abc 就是一个前缀。怎么实现的呢?
首先,我们需要定义一个结构体,用来定义这个“前缀”。
一. 定义结构体
struct Abc<Base> {
var base: Base
init(_ base: Base) {
self.base = base
}
}
结构体的名称就是前缀的名称。 定义了一个泛型,因为我们想让这个前缀
能够在泛型中使用。也就是说因为这个泛型,我们的前缀
不仅在 String 类型,在其它类型、甚至是自定义类型中也能使用。
base 属性是一个泛型类型,它的类型暂时不能确定,需要在 init 方法中确定。
在 init 方法中,可以传入一个对象,并用该对象的类型来初始化泛型类型。同时将这个对象保存到 base 属性中以便以后用到。
为了方便使用,我们先定义一个协议,然后利用协议扩展提供一个计算属性:
protocol AbcProtocol {}
extension AbcProtocol {
var abc: Abc<Self> { // 1
set {} // 2
get { Abc(self) } // 3
}
}
- 定义一个计算属性,其类型是我们前面定义的结构体。
- 必须,否则编译器报错。
- 实例化 Abc 对象返回。同时将 self 传入到 base 属性,同时明确泛型的类型。
这样当我们对其它类型进行扩展时可以适用这个协议,这样就有一个现成的 abc 属性了。然后在通过扩展 Abc 结构体来添加新的方法。
二. 定义扩展
假设我们要为 String 扩展一个 test 的方法,那么我们可以这样定义一个扩展:
extension String: AbcProtocol{} // 1
extension NSString: AbcProtocol() // 2
extension Abc where Base: ExpressibleByStringLiteral{ // 3
func test() {
print("\(base as! String)") // 4
}
}
- 明确 String 继承了 AbcProtocol,获得 abc 属性。
- 明确 NSString 继承了 AbcProtocol,因为 NSMutableString 继承自 NSString,所以也自动获得了 abc 属性。
- 对 Abc 结构体进行扩展,并指明了这个扩展只能应用于实现了 ExpressibleByStringLiteral 协议的类型。所有能够用字符串字面量进行赋值的类型都遵守了ExpressibleByStringLiteral协议,比如 String、NSString。因此这个扩展可同时用于 String 和 NSString\NSMutableString。
- 打印 base 的内容。因为 base 有可能是 NSString,所以可以先转为 String 类型。
当然,我们还可以修改泛型约束的条件,让这个扩展(前缀
)也可以应用到其它类型:
extension Int: AbcProtocol{}
extension Abc where Base == Int {
func test2() {
print("\(base)")
}
}
这样就实现了我们的扩展前缀功能。现在,要调用扩展中的方法,就必须加上 abc 前缀了:
"ABC123456".abc.test()
10.abc.test2()
扩展 static 方法
如果需要扩展 static 方法,那么就需要进一步扩展 Abc 结构体,让它再增加一个静态的 abc 属性:
extension AbcProtocol {
var abc: Abc<Self> {
set {}
get { Abc(self) }
}
static var abc: Abc<Self>.Type { // 1
set {} // 2
get { Abc<Self>.self } // 3
}
}
- 定义一个 static 的计算属性,其类型是 Abc.self 的 Type。
- 必须,否则编译器报错。
- 返回 Abc.self 的类型(非实例)。同时将 self 传入到 base 属性,同时明确泛型的类型。
在 Abc 的扩展中定义 static 方法,然后调用 static 方法:
String.abc.test()