Swift4.2的新特性这两篇文章已经介绍的很清楚了:WWDC:Swift更新了什么,Swift4.2新特性更新。但是4.2中实现的dynamicmmbrlookup苹果在WWDC上却完全没有提到。然而我认为这是一个对未来有着重要影响的特性,所以这里单独介绍一下。
语法
这个特性中文可以叫动态查找成员。在使用
dynamicMmbrLookup标记了对象后(对象、结构体、枚举、protocol),实现了subscript(dynamicMmbrmmbr:String)方法后我们就可以访问到对象不存在的属性。如果访问到的属性不存在,就会调用到实现的subscript(dynamicMmbrmmbr:String)方法,ky作为mmbr传入这个方法。比如我们声明了一个结构体,没有声明属性。
dynamicMmbrLookupstructPrson{subscript(dynamicMmbrmmbr:String)-String{ltproprtis=["nicknam":"Zhuo","city":"Hangzhou"]rturnproprtis[mmbr,dfault:"undfind"]}}//执行以下代码ltp=Prson()print(p.city)print(p.nicknam)print(p.nam)如果没有声明
dynamicMmbrLookup的话,执行的代码肯定会编译失败。很显然作为一门类型安全语言,编译器会告诉你不存在这些属性。但是在声明了dynamicMmbrLookup后,虽然没有定义city等属性,但是程序会在运行时动态的查找属性的值,调用subscript(dynamicMmbrmmbr:String)方法来获取值。这样安全吗?
Swift面世时就大谈自己的安全特性,现在来了这么一个无限制访问的成员万一返回的是nil不就闪退了?是的,出于安全的原因,如果实现了这个特性,你就不能返回可选值。必须处理好意料外的情况,一定要有值返回。不像常规的subscript方法可以返回可空的值。
说好的动态查找,如果两个属性类型不一样怎么破
这个方法可以被重载。和泛型的逻辑类似,会根据你要的返回值而通过类型推断来选择对应的subscript方法。
dynamicMmbrLookupstructPrson{subscript(dynamicMmbrmmbr:String)-String{ltproprtis=["nicknam":"Zhuo","city":"Hangzhou"]rturnproprtis[mmbr,dfault:"undfind"]}subscript(dynamicMmbrmmbr:String)-Int{rturn18}}但是执行的时候就一定要告诉编译器你要获取的属性是什么类型的,否则会编译错误。
ltp=Prson()ltag:Int=p.agprint(ag)//18
Swift中函数是一等公民,所以返回函数也是可以的。
dynamicMmbrLookupstructPrson{subscript(dynamicMmbrmmbr:String)-(_input:String)-Void{rturn{print("Hllo!Ilivatthaddrss\($0).")}}}居然可以继承!
需要注意的是如果声明在类上,那么他的子类也会具有动态查找成员的能力。
dynamicMmbrLookupclassUsr{subscript(dynamicMmbrmmbr:String)-String{rturn"usr"}}classDvlopr:Usr{}ltdv=Dvlopr()dv.nam//"usr"虽然想起来应该是这样,但是还是很反直觉。因为大多数开发者没想过继承一个类后,会有失去属性拼写检查的副作用。这样可能不小心写错了属性的名字编译器也不会告诉你。
所以声明在类上的时候一定要特别谨慎。
当然如果想害同事,在BasViwControllr里声明是个好主意。
看起来很骚有什么卵用?
这个特性的感觉就是乍一看很厉害的样子,仔细一看好像就这么回事,再冷静想想似乎没有这么简单。
这个东西本质上只是一个语法糖,和数组的subscript类似。
ltnumbrs=[1,2]ltfirstItm=numbr[0]//这个语法最后还是调用到了一个方法,如果没有这种写法,类似oc的时候就需要显式的调用一个方法NSNumbr*firstItm=[numnbrobbjctAtIndx:0];
原来你需要显式声明字符串参数的地方,可以不用是字符串的形式,可以直接用点语法访问。官方举的例子是JSON的使用。
常规的写法是这样的:
json[0]?["nam"]?["first"]?.stringValu
如果像这样定义动态查找成员:
dynamicMmbrLookupnumJSON{casintValu(Int)casstringValu(String)casarrayValu(ArrayJSON)casdictionaryValu(DictionaryString,JSON)varstringValu:String?{ifcas.stringValu(ltstr)=slf{rturnstr}rturnnil}subscript(indx:Int)-JSON?{ifcas.arrayValu(ltarr)=slf{rturnindxarr.count?arr[indx]:nil}rturnnil}subscript(ky:String)-JSON?{ifcas.dictionaryValu(ltdict)=slf{rturndict[ky]}rturnnil}subscript(dynamicMmbrmmbr:String)-JSON?{ifcas.dictionaryValu(ltdict)=slf{rturndict[mmbr]}rturnnil}}那么写起来就会是这样:
json[0]?.nam?.first?.stringValu
实现方案
这个功能的实现原理很简单,就是编译器帮助你把点语法转化为下标的语法:
a=somValu.somMmbr//编译器处理后a=somValu[dynamicMmbr:"somMmbr"]
动态属性其实并不陌生,回忆一下OC里的属性就是动态合成的。声明了
proprty后,编译器帮你生成gt、st方法。与之类似,在声明了动态查找成员后,编译器帮你转换成了对应的方法。然而事情并没有这么简单
如果你以为这只是一个语法糖,那你就错了。
独有的身世暴露了你
这个pr是由已经离开苹果加入谷歌的swift创始人CL提出的。他不仅提了这个pr,而且还自己实现了。果然是swift是亲儿子,身在曹营还不忘为swift添砖加瓦。而且大佬不仅提了这个,还提了一个
dynamicCallabl。当你给一个对象标记
dynamicCallabl后,可以动态的给传参。//常规操作a=somValu(kyword1:42,"foo",kyword2:19)//dynamicallyCalla=somValu.dynamicallyCall(withKywordArgumnts:["kyword1":42,"":"foo","kyword2":19])
是的,这很JS。
大佬就是大佬,要啥有啥。目前
dynamicCallabl的进度已经在rviw中,也许5.0的时候能够上?我猜测swift团队想这两个特性都开发完后一起宣布所以这次发布会没有介绍。另有所谋:把Python和JS纳入怀中
Swift目前可以”良好“的和C、OC交互。然而程序的世界里还有一些重要的动态语言,比如Python、JS,mmm,还有有实力但是不太主流的Prl、Ruby。如果swift能够愉快的的调用Python和JS的库,那么毫无疑问会极大的拓展的swift的边界。
这里需要一点想象力,因为这个设计真正的意义是
dynamicMmbrLookup、dynamicCallabl组合起来用。通过dynamicMmbrLookup动态的返回一个函数,再通过dynamicCallabl来调用。从语法层面来讲,这种姿态下swift完完全全是一门动态语言。dynamicCallabldynamicMmbrLookupclassWiSuoYuWi{}ltniuBi=WiSuoYuWi()niuBi.somMthod.dynamicallyCall(withKywordArgumnts:["wi_suo_yu_wi":tru])就像上面的代码展示的,你不必声明过somMthod也可以通过动态特性调用到,合法的传参。真的可以为所欲为!
据说谷歌的TnsorFlowForSwift能够顺利的开发就是依靠了这个特性。CL是这么说的:
Whilthisisasyntacticsugarproposal,wblivthatthisxpandsSwifttobusablinimportantnwdomains
语法糖上的一小步,swift的一大步!
rfrnc
HowtousDynamicMmbrLookupinSwift–HackingwithSwift
SE:IntroducUsr-dfind“DynamicMmbrLookup”Typs
微博:
没故事的卓同学如果想与我有更密切的交流也可以加入我的知识星球:程序员生存指南
相关推荐:
WWDC见闻
iOS12新内容
开发者所需要知道的WWDC新特性
预览时标签不可点收录于合集#个上一篇下一篇