这儿每天共享一个 iOS 的新知识,快来重视我吧
前言
Swift 5.9 出了一个新功能,Swift Parameter Packs(Swift 参数包),在之前的文章中也有提到过,不过其时没讲参数包应该怎么运用,今日就来学习一下什么是参数包,以及在日常开发中有哪些运用场景。
晋级 Xcode 15 之后,你能用到哪些 swift 新特性
什么是 Swift Parameter Packs
咱们用一个例子来说明,当咱们在函数中运用泛型时,可能是这样的:
funceachFirst<T>(_item:T)->T?
当咱们运用两个泛型时:
funceachFirst<T1,T2>(_item1:T1,_item2:T2)->(T1?,T2?)
当咱们运用三个泛型时:
funceachFirst<T1,T2,T3>(_item1:T1,_item2:T2,_item3:T3)->(T1?,T2?,T3?)
大家发现问题了吗?当运用的泛型越来越多,就会使得函数越来越长,越来越丑。
在 SwiftUI 结构中 buildBlock
函数竟然多达 10 个泛型:
staticfuncbuildBlock<C0,C1,C2,C3,C4,C5,C6,C7,C8,C9>(_c0:C0,_c1:C1,_c2:C2,_c3:C3,_c4:C4,_c5:C5,_c6:C6,_c7:C7,_c8:C8,_c9:C9)->TupleView<(C0,C1,C2,C3,C4,C5,C6,C7,C8,C9)>whereC0:View,C1:View,C2:View,C3:View,C4:View,C5:View,C6:View,C7:View,C8:View,C9:View
这看起来是不是十分令人头疼,swift 为了处理这个问题,提出了 Swift Parameter Packs 的概念,简单来讲便是,能够用 each
关键字来替代那些重复的代码,运用 each 之后的 buildBlock 函数变为:
staticfuncbuildBlock<eachContent>(_content:repeateachContent)->TupleView<(repeateachContent)>whererepeateachContent:View
对比一下是不是简洁多了。
接下来讲几个实用的例子。
KeyPath 取值
比方我有个用户类,然后我想要取其中的特点,并以元组的方式回来:
classUser{
letid:String
letname:String
letage:Int
init(id:String,name:String,age:Int){
self.id=id
self.name=name
self.age=age
}
}
funcvalueAt(_object:User,keyPath1:KeyPath<User,String>,keyPath2:KeyPath<User,String>,keyPath3:KeyPath<User,Int>)->(String,String,Int){
return(object[keyPath:keyPath1],object[keyPath:keyPath2],object[keyPath:keyPath3])
}
letuser=User(id:"0",name:"iOS新知",age:1)
print(valueAt(user,keyPath1:.id,keyPath2:.name,keyPath3:.age))
实际上,这种写法既不高雅,也很费事,还不通用。
运用参数包的方式改一下:
funcvaluesAt<T,eachU>(_subject:T,keyPathskeyPath:repeatKeyPath<T,eachU>)->(repeateachU){
(repeat(subject[keyPath:eachkeyPath]))
}
修正完调用只需求:
valuesAt(user,keyPaths:.id,.name,.age)
后面的参数能够传恣意多个,而且运用泛型之后不仅 User 能够调用,其他的类也能运用。
缓存性能开支大的函数
有一些函数可能需求时间比较久,或者性能开支比较大,这时候的优化方案一般需求把操作过的缓存起来,不要每次都重复执行,比方加载图片比较耗时,那么就需求把加载过的图片缓存起来,那么你可能有这样的函数:
funcfetchImage(url:String)->UIImage{
ifletresult=storage[url]{
returnresult
}else{
//TODO:加载图片
returnxxx
}
}
这个办法欠好的地方在于,它只适用于你当下图片的场景,能够把这一类需求运用参数包给封装起来:
funcmemoize<eachArgument:Hashable,Return>(
_function:@escaping(repeateachArgument)->Return
)->(repeateachArgument)->Return{
varstorage=[AnyHashable:Return]()
return{(argument:repeateachArgument)in
varkey=[AnyHashable]()
repeatkey.append(AnyHashable(eachargument))
ifletresult=storage[key]{
returnresult
}else{
letresult=function(repeateachargument)
storage[key]=result
returnresult
}
}
}
这个函数要求传入一个函数,并回来一个新函数,那么上边缓存图片的例子就能够用这个函数实现:
//loadImage是加载图片的函数
letmemoizedLoadImage=memoize(loadImage)
memoizedLoadImage(URL(filePath:"some-url"))
memoizedLoadImage(URL(filePath:"some-url"))
memoizedLoadImage(URL(filePath:"other-url"))
当调用 memoizedLoadImage
函数时,函数体内会先从 storage
查看,假如存在直接回来,假如不存在,则调用传入的 loadImage
办法来获取。
装修高阶函数
能够运用参数包把高阶函数封装起来,以便通用,举个例子:
funcdecorateAround<eachArgument,Return>(
_function:@escaping(repeateachArgument)->Return,
around:@escaping((repeateachArgument)->Return,repeateachArgument)->Return
)->(repeateachArgument)->Return{
{(argument:repeateachArgument)in
around(function,repeateachargument)
}
}
它承受两个参数:function
和 around
,而且回来一个新的函数,这个新函数也承受与原函数相同的参数,并将它们传递给 around
函数。适当所以把 function
装修了一下。
听起来比较绕对吧?来看个例子:
funcaddTwoNumbers(_a:Int,_b:Int)->Int{
returna+b
}
//创建一个装修函数,用于在调用原始函数之前和之后输出信息
letdecoratedAdd=decorateAround(addTwoNumbers){(originalFunction,a,b)in
print("调用addTwoNumbers之前")
letresult=originalFunction(a,b)
print("调addTwoNumbers之后")
returnresult
}
decoratedAdd(1,2)//打印3
decorateAround
函数用于创建一个装修函数 decoratedAdd
,我在该函数在调用 addTwoNumbers
的前后输出两段信息。最后,我调用了装修后的函数,观察输出的信息。
这儿每天共享一个 iOS 的新知识,快来重视我吧
本文同步自微信公众号 “iOS新知”,每天准时共享一个新知识,这儿只是同步,想要及时学到就来重视我吧!