Swift新特性 dynamicMemberLookup和dynamicCallable

栏目: Swift · 发布时间: 6年前

内容简介:dynamicMemberLookup是Swift4.2里更新的一个特性翻译出来就是动态成员查找。在使用@dynamicMemberLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)方法,key 作为 member 传入这个方法。例如:输出的结果为 s

dynamicMemberLookup是Swift4.2里更新的一个特性翻译出来就是动态成员查找。在使用@dynamicMemberLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)方法,key 作为 member 传入这个方法。

例如:

@dynamicMemberLookup
 class Test {
 
 subscript (dynamicMember member: String) -> String {
 return "12321321"
 }
 
 subscript (dynamicMember member: String) -> Int {
 return 455
 }
 
 }
 
 let t = Test()
 
 var s:String = t.name
 var p: Int = t.age

 print(s);
 print(p);
复制代码

输出的结果为 s = "12321321",p = 455

我再这个类里面并没有显示的声明 name 和 age 这两个属性但是他却可以得到这两个属性。是因为当我将这个类标记为 @dynamicMemberLookup 类里面会实现**subscript (dynamicMember member: String) -> ?**这个方法。

如果没有声明@dynamicMemberLookup的话,执行的代码肯定会编译失败。很显然作为一门类型安全语言,编译器会告诉你不存在这些属性。但是在声明了@dynamicMemberLookup后,虽然没有定义 age等属性,但是程序会在运行时动态的查找属性的值,调用subscript(dynamicMember member: String)方法来获取值。

这个属性可以被重载,会根据你要的返回值而通过类型推断来选择对应的subscript方法。例如

@dynamicMemberLookup
struct Person {
     subscript(dynamicMember member: String) -> String {
        let properties = ["name": "Swift", "city": "B"]
        return properties[member, default: ""]
    }

    subscript(dynamicMember member: String) -> Int {
        return 18
    }
}

let p = Person()
/***声明常量必须声明类型*/
let test:String = p.k;
print(p.nickname)
print(p.city)
print(test);
print(p.age)
复制代码

输出的结果为 "Swift","b","undefined",18。 执行的时候一定要告诉编译器你的常量是什么类型的。

@dynamicMemberLookup有啥用

我们知道了dynamicMemberLookup是什么怎么用,但是苹果为啥要推出这样一种语法糖。

官方给出的 例子 是这样的

@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
复制代码

它是将自定义下标转换为简单点语法的语法糖。 其实相当于执行了 json[0].name == json[0].subscript(dynamicMember member: "name")

通过这个方法拿到 json[0]字典key为name对应的值

subscript(dynamicMember member: String) -> JSON? {
      if case .dictionaryValue(let dict) = self {
         return dict[member]
      }
      return nil
   }
复制代码

这个只是简单的应用 在Swift5.0里又推出了 dynamicCallable 这个特性。可以动态的进行传参。

dynamicCallable

@dynamicCallable是什么

SE-0216 向@dynamicCallable 添加了一个新的@dynamicCallable属性,该属性带来了将类型标记为可直接调用的能力。它是语法糖,而不是任何类型的编译器,有效地转换此代码:

let result = random(numberOfZeroes: 3)

let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])
复制代码

之前,在Swift 4.2 中写了一个叫做@dynamicMemberLookup的功能。@dynamicCallable是@dynamicMemberLookup的自然扩展,@dynamicMemberLookup并且具有相同的目的:使 Swift 代码更容易与动态语言(如 Python 和 JavaScript)一起工作 要将此功能添加到自己的类里,需要添加@dynamicCallable属性加上以下一@dynamicCallable种或两种方法:

func dynamicallyCall(withArguments args: [Int]) -> Double

func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double

复制代码

第一种是在调用没有参数标签的类型时使用的,第二种是在提供标签时a(b, c)使用的(例如a(b: cat, c: dog) ). @dynamicCallable非常灵活地了解其方法接受和返回的数据类型,让您从 Swift 的所有类型安全性中获益,同时仍有一些可高级使用空间。因此,对于第一个方法(没有参数标签),您可以使用任何符合ExpressibleByArrayLiteral的任何方法,如数组、数组切片和集;对于第二种方法(带有参数标签),您可以使用任何符合ExpressibleByDictionaryLiteral文本,如字典和键值对。

注意:如果您以前没有使用过KeyValuePairs那么现在正是了解它们的好时机,因为它们@dynamicCallable非常有用。

KeyValuePairs在 Swift 5.0 之前,有点令人困惑地称为DictionaryLiteral是一种有用的数据类型,它提供了类似字典的功能,具有以下几个优点:

  1. 您的密钥不需要符合Hashable.
  2. 您可以使用重复的键添加项。(不会覆盖自定中添加的值)
  3. 添加项的顺序将保留。(是DictionAry变有序)

除了接受各种输入外,您还可以为各种输出提供多个重载 - 一个输出可以返回一个字符串,一个返回一个整数,等等。只要 Swift 能够解决使用哪一个,就可以混合和匹配所有您想要的。

下面是一个 例子 :

首先,下面是一个RandomNumberGenerator结构,根据传入的输入,生成介于 0 和特定最大值之间的数字:

struct RandomNumberGenerator {
    func generate(numberOfZeroes: Int) -> Double {
        let maximum = pow(10, Double(numberOfZeroes))
        return Double.random(in: 0...maximum)
    }
}

let random = RandomNumberGenerator()
let result = random.generate(numberOfZeroes: 0)
复制代码

要将其切换到@dynamicCallable我们将@dynamicCallable编写类似内容:

@dynamicCallable
struct RandomNumberGenerator {
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Double {
        let numberOfZeroes = Double(args.first?.value ?? 0)
        let maximum = pow(10, numberOfZeroes)
        return Double.random(in: 0...maximum)
    }
}

let random = RandomNumberGenerator()
/// numberOfZeroes 可以自定义
/// let result = random.dynamicallyCall(withKeywordArguments: ["numberOfZeroes": 3])
/// let result = random(numberOfZeroes: 3)

let result = random(numberOfZeroes: 0)
复制代码

@dynamicCallable使用注意

@dynamicCallable时需要注意一些重要的规则:

  1. 您可以将其应用于结构、枚举、类和协议。
  2. 如果使用**withKeywordArguments: 并且不使用 withArguments:**您的类型仍然可以在没有参数标签的情况下调用 - 您只会获得键的空字符串。
  3. 如果withKeywordArguments:或与withArguments:被标记为 throwing ,调用类型也将 throwing
  4. 不能@dynamicCallable添加到扩展 ,只能添加类型的主要定义。
  5. 您仍然可以向类型添加其他方法和属性,并正常使用它们。

总结

dynamicMemberLookup是Swift4.2里更新的一个特性翻译出来就是动态成员查找。在使用@dynamicMemberLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMember member: String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的 subscript(dynamicMember member: String)方法,key 作为 member 传入这个方法。 ynamicCallable属性,该属性带来了将类型标记为可直接调用的能力。它是语法糖

Swift 目前可以”良好“的和 C、OC 交互。然而程序的世界里还有一些重要的动态语言,比如 Python 、 JS,emmm,还有有实力但是不太主流的 Perl 、Ruby。如果 swift 能够愉快的的调用 Python 和 JS 的库,那么毫无疑问会极大的拓展的 swift 的边界。 这里需要一点想象力,因为这个设计真正的意义是@dynamicMemberLookup、 @dynamicCallable组合起来用。通过@dynamicMemberLookup动态的返回一个函数,再通过@dynamicCallable来调用。从语法层面来讲,这种姿态下 swift 完完全全是一门动态语言。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning Processing

Learning Processing

Daniel Shiffman / Morgan Kaufmann / 2008-08-15 / USD 49.95

Book Description Teaches graphic artists the fundamentals of computer programming within a visual playground! Product Description This book introduces programming concepts in the context of c......一起来看看 《Learning Processing》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试