前置知识:可变参数

Posted Karl-hut

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前置知识:可变参数相关的知识,希望对你有一定的参考价值。

 

 

 

 

 

 

Swift基础知识碎片

1.函数的可变参数

同一个方法中只能有一个可变参数,而且不限制可变参数在所有参数中的位置。在OC中可变参数只能作为方法中参数的最后一个。

func sum(input: Int...) -> Int 
    return input.reduce(0, +)

sum(input: 1,2,3,4,5)	// 15

2.inout

func switching(a: inout Int, b: inout Int) 
    (a, b) = (b, a)


var a = 10
var b = 20
switching(a: &a, b: &b)

inout表示Swift的可修改参数。而且在这个例子中,通过tuple是a和b的值直接进行了交换,而在OC中我们还需要使用一个第三者变量来进行进行临时存储。

3.柯里化

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回一个新的函数,这个函数能够处理剩余参数。
你可以理解它为一种返回值为闭包的函数,或者理解为一种函数对函数的调用。

看下面的例子:

func someFunc(_ index: Int) -> (Int) -> Int 
    return  num in
        return num + index
    

let newFunc = someFunc(10)
let result1 = newFunc(5)	// 15

let result2 = someFunc(9)(8)	// 17

再来看一个比大小的例子:

func firstMoreThanSecond(_ num: Int) -> (Int) -> Bool 
    return $0 > num


let result3 = firstMoreThanSecond(10)(11)	// false 
let result4 = firstMoreThanSecond(8)(10)	// true

柯里化带来的优势:
对于柯里化,它可以将你的函数碎片化,将函数分层调用,所以提高了代码灵活性、复用性、降低依赖,而且代码也会相对简洁。
总之,柯里化可以很好地体现出函数式编程思想。

最后借用Instance Methods are “Curried” Functions in Swift中的一个例子,体会体会好坏。

protocol TargetAction 
    func performAction()


struct TargetActionWrapper<T: AnyObject>: TargetAction 
    weak var target: T?
    let action: (T) -> () -> ()
    
    func performAction() -> () 
        if let t = target 
            action(t)()
        
    


enum ControlEvent 
    case TouchUpInside
    case ValueChanged
    // ...



class Control 
    var actions = [ControlEvent: TargetAction]()
    
    func setTarget<T: AnyObject>(target: T,
                                 action: @escaping (T) -> () -> (), controlEvent: ControlEvent) 
        
        actions[controlEvent] = TargetActionWrapper(
            target: target, action: action)
    
    
    func removeTargetForControlEvent(controlEvent: ControlEvent) 
        actions[controlEvent] = nil
    
    
    func performActionForControlEvent(controlEvent: ControlEvent) 
        actions[controlEvent]?.performAction()
    



class DCSnail 
    func excute() 
        let button = Control()
        button.setTarget(target: self, action: DCSnail.tapAction, controlEvent: .TouchUpInside)
        button.performActionForControlEvent(controlEvent: .TouchUpInside)
    
    
    func tapAction() 
        print("tapped!!!")
    


DCSnail().excute()

4.函数嵌套

这里所说的函数嵌套与柯里化中所提到的函数分层调用不是一个概念,而是在函数中继续定义函数。
来看下面一个函数:

func generateObjec(type: Int) -> String 
    if 0 == type 
        return zeroType()
     else if 1 == type 
        return oneType()
     else 
        return defaultType()
    


func zeroType() -> String 
    return "Zero"


func oneType() -> String 
    return "One"


func defaultType() -> String 
    return "Two"

如果使用函数嵌套将会如下效果:

func generateObjec(type: Int) -> String 
    func zeroType() -> String 
        return "Zero"
    
    func oneType() -> String 
        return "One"
    
    func defaultType() -> String 
        return "Two"
    
    
    if 0 == type 
        return zeroType()
     else if 1 == type 
        return oneType()
     else 
        return defaultType()
    

函数嵌套在你函数主体内容过长,且本模块的功能与外部逻辑没有任何关系时,能发挥非常大的作用。它会使你的单个函数不在冗长,且将它们分成几个小型的模块,且定义在主函数之内,并不影响外部的关系。
所以这样的访问权限和这样的模块化会提高代码可读性和维护性。

5.闭包中的可选链

class A 
    func block() 
        //...
    


class B 
    var letter: A?

let b = B()
let optionResult = b.letter?.block()	// nil

B类中有个可选类型的letter变量,当通过可选链对其函数进行调用时,将会得到nil。这是毋庸置疑的,那么继续看:

let bBlock1 = (b: B) -> () in
    b.letter?.block()

let bBlock2 = (b: B) -> ()? in
    b.letter?.block()


bBlock1(b)	// ()
bBlock2(b)	// nil

新建了两个block,它们的返回值分别为()()?,最后将b传入得到的结果却是分别为()nil。这个结果够不够出乎你的意料?

在包含可选链的block中,返回值是()或者Void的,返回值永远都不为nil;而返回值是()?或者Void?返回值为nil。所以虽然()Void是一回事,但在这里并不是一回事。

6.@autoclosure

假如有一个尾随闭包参数的函数,只是根据闭包的返回值判断真假:

func trueOrFalse(_ condition: () -> Bool) 
    if condition() 
        print("pass")
    

那么你可以定义个闭包来进行传值:

let conditionBlock =  () -> (Bool) in
    return 2 > 1

你也可以直接使用尾随闭包的属性,而且还可以让闭包从语境中推断类型,一直简化:

trueOrFalse( return 2 > 1 )
trueOrFalse( 2 > 1 )

最后一直简化成这样:

trueOrFalse 2 > 1 

好了,引入正题,将入使用了@autoclosure对闭包进行修饰:

func trueOrFalse2(_ condition: @autoclosure () -> Bool) 
    if condition() 
        print("pass")
    

我们可以这样使用:

trueOrFalse2(2 > 1)

这是因为,@autoclosure修饰的闭包可以自动将 2>1转换为()->bool,以供函数使用。

7.@escaping

func perform(block: ()->()) 
    block()


func performAsync(block: @escaping ()->()) 
    DispatchQueue.main.async 
        block()
    


class Test 
    var ID = "id"
    
    func method1() 
        perform 
            print(ID)
        
        print("before method1")
        ID = "method1"
    
    
    func method2() 
        performAsync 
            print(self.ID)
        
        print("before method2")
        ID = "method2"
    
    
    func method3() 
        performAsync 
            [weak self] in
            print(self?.ID ?? "nil")
        
        print("before method3")
        ID = "method3"
    

Test().method1()	// id
Test().method2()	// method
Test().method3()	// nil

在形式参数前写 @escaping 来明确闭包是允许逃逸的,闭包可以逃逸的一种方法是被储存在定义于函数外的变量里。所以一定要注意启动异步任务回调所带来对调用时机的不同,要在合适的作用域和时机中对持有的变量进行使用。
另外,关于@escaping还需要注意,当你在协议或者父类中包含一个@escaping的闭包,那么在实现协议或者父类的子类中,该方法也必须被声明为@escaping,否则两个方法会被认为为两个函数。

8.自定义字面量(Literal)

Swift 为我们提供了一组非常有用的接口,用来将字面量转换为特定的类型。

ExpressibleByArrayLiteral
ExpressibleByBooleanLiteral
ExpressibleByDictionaryLiteral
ExpressibleByFloatLiteral
ExpressibleByIntegerLiteral
ExpressibleByStringLiteral
ExpressibleByUnicodeScalarLiteral
ExpressibleByExtendedGraphemeClusterLiteral

ExpressibleByStringLiteral为例,来自定义字符串的字面量:

class Dog: ExpressibleByStringLiteral 
    let name: String
    init(name value: String) 
        self.name = value
    
    
    required convenience init(stringLiteral value: String) 
        self.init(name: value)
    
    
    required convenience init(extendedGraphemeClusterLiteral value: String) 
        self.init(name: value)
    
    
    required convenience init(unicodeScalarLiteral value: String) 
        self.init(name: value)
    

let dog: Dog = "Husky"
dog.name	// Husky

相关资料:
Swift 之关键字总结上篇
Swift 之关键字总结下篇

以上是关于前置知识:可变参数的主要内容,如果未能解决你的问题,请参考以下文章

C基础知识(12):可变参数

Swift基础知识碎片

Swift基础知识碎片

Python基础知识详解

Java基础知识回顾-22(静态导入,可变参数,Collections集合工具类,集合嵌套)

Java可变参数