@dynamicMemberLookup
dynamicMemberLookup是Swift4.2里更新的一个特性翻译出来就是动态成员查找。在使用@dynamicMemberLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)方法,key 作为 member 传入这个方法。
//动态成员查找
@dynamicMemberLookup
struct Test {
subscript (dynamicMember member: String) -> String {
return "测试"
}
subscript (dynamicMember member: String) -> Int {
return 321
}
}
let t = Test()
let name:String = t.name
let age:Int = t.age
print(name,age)
输出结果为测试 321
。可以看到,我在Test这个结构体里并没有声明name和age这两个属性,但是却依然获取了这两个属性,就是因为这个Test这个struct@dynamicMemberLookup标记之后,我们又实现了subscript (dynamicMember member: String) -> String这个方法之后,这就告诉程序要在运行时动态的查找属性的值,调用subscript (dynamicMember member: String) -> String方法获取值。注意:执行的时候要声明常量类型,否则编译会失败。
我们再来看下官方给出的demo:
@dynamicMemberLookup
enum JSON {
case intValue(Int)
case stringValue(String)
case arrayValue(Array<JSON>)
case dictionaryValue(Dictionary<String, JSON>)
var stringValue: String? {
if case .stringValue(let str) = self {
return str
}
return nil
}
subscript(index: Int) -> JSON? {
if case .arrayValue(let arr) = self {
return index < arr.count ? arr[index] : nil
}
return nil
}
subscript(key: String) -> JSON? {
if case .dictionaryValue(let dict) = self {
return dict[key]
}
return nil
}
subscript(dynamicMember member: String) -> JSON? {
if case .dictionaryValue(let dict) = self {
return dict[member]
}
return nil
}
}
如果我们想取json里面的值则需要
let json = JSON.stringValue("Example")
json[0]?["name"]?["first"]?.stringValue
如果我们使用了@dynamicLookUp,就可以这么用json[0]?.name?.first?.stringValue
@dynamicCallable
Swift5又增加了@dynamicCallable,它是语法糖,而不是编译器魔法。它将类型标记为可直接调用,主要是想Swift代码更好的和Python、js等动态语言一起更好的工作。
要想使用@dynamicCallable,需要标记@dynamicCallable并实现一下方法:
func dynamicCall(withArguments args: [Int]) -> Double
func dynamicCall(withKeywordArguments args: KeyValuePairs <String,String>) -> String
当你调用没有参数标签的类型(例如a(b,c))时会使用第一个,而当你提供标签时使用第二个(例如a(b:cat,c:dog))。@dynamicCallable对于接受和返回的数据类型非常灵活,使您可以从Swift的所有类型安全性中受益,同时还有一些高级用途。因此,对于第一种方法(无参数标签),您可以使用符合ExpressibleByArrayLiteral的任何内容,例如数组,数组切片和集合,对于第二种方法(使用参数标签),您可以使用符合ExpressibleByDictionaryLiteral的任何内容,例如字典和键值对。除了接受各种输入外,您还可以为输出提供多个重载,一个可能返回字符串,一个是整数,依此类推。只要Swift能够解决使用哪一个,你就可以混合搭配你想要的一切。代码:
@dynamicCallable
struct RandomNumber {
func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double {
print("args:\(args)")
let numberOfZeroes = Double(args.first?.value ?? 0)
let maximum = pow(10, numberOfZeroes)
return Double.random(in: 0...maximum)
}
}
let random = RandomNumber()
let result = random(numberOfZeroes:5)
print("result:\(result)")
使用@dynamicCallable时需要注意:可以将它应用于结构、枚举、类和协议,如果你实现withKeywordArguments:并且没有实现withArguments:,你的类型仍然可以在没有参数标签的情况下调用,你只需要将键设置为空字符串就行。如果withKeywordArguments:或withArguments:被标记为throw,则调用该类型也会throw。不能将@dynamicCallable添加到扩展,只能添加在类型的主要定义中。可以为类型添加其他方法和属性,并正常使用它们。另外,它不支持方法解析,这也就是说我们必须直接调用类,random(numberOfZeroes: 5),而不是调用类型上的方法(例如random.generate(numberOfZeroes: 5)。
网友评论