Skip to content

Swift默认参数 & 可变长参数

liuzhiyi1992 edited this page Apr 9, 2016 · 1 revision

Swift相比于oc有很多方便的新特性。今天我们来介绍下Swift中的默认参数(default parameter)和可变长参数(variable parametric)。 这篇文章刚起笔的时候的题目其实是这样的:"Swift可选参数带来的便利",组织了一会发现不太对劲,怎么就把人家的名字起成了可选参数,如果扯可选参数的话,函数的overload就有点这样的感觉。

##默认参数
ok,我们先说说Swift中的默认参数,很简单,swift允许方法声明含有默认值的参数,也就是说方法的接收参数可以存在一个默认值,这个默认值对调用者保密,隐藏在方法实现内,在invoke function的时候若无传入则使用默认值,有则覆盖,用于便利开发,不会带来任何影响。
我们用SpreadButton中的一段代码来做例子,去年曾有位朋友就这段代码专门来问过我:

//declaration:
func movingPath(startPoint: CGPoint, 
                  endPoint: CGPoint, 
                startAngle: CGFloat, 
                  endAngle: CGFloat, 
                    center: CGPoint, 
                     shock: Bool = false) -> UIBezierPath {
    let path = UIBezierPath()
    path.moveToPoint(startPoint)
    path.addLineToPoint(endPoint)

    if shock {
        //do something
    }
    return path
}

//invoke-1
animationPath = movingPath(btn.layer.position, endPoint: startOutSidePoint, 
                                             startAngle: startAngle/180*π, 
                                               endAngle: angle/180*π, 
                                                 center: btn.layer.position)

//invoke-2
animationPath = movingPath(btn.layer.position, endPoint: startOutSidePoint, 
                                             startAngle: startAngle/180*π, 
                                               endAngle: angle/180*π, 
                                                 center: btn.layer.position, 
                                                  shock: true)

-movingPath()参数列表的末尾有一个default parameter(shock),决定我这个控件的动画路径是否包含震动路径,在//invoke-1中利用了这个缺省,方法体内shock的值即为false,而//invoke-2中传入shock=true改变了这个缺省。
顺便一提这里的业务需求是多个Path Button的展开,只有最后一个需要有震动路径,于是我只在计算最后一个Path Button的时候才传入了一个参数shock=true。

如果初学者朋友们不想被我的代码打搅理解,这里可以贴一段Apple官方文档中的代码缩减版,很简单:

func someFunction(parameterWithDefault: Int = 12) {
    // function body goes here
    // if no arguments are passed to the function call,
    // value of parameterWithDefault is 12
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12

而对于default parameter位置灵活性会不会像历史语言设计那样死板地只能跟在队尾呢?手痒的朋友可以自己试试。哈哈。


##可变长参数 记得上一次使用可变长参数也是在[SpreadButton]()中。。。好吧不说他了,再对上一次就是小弟还在做Android开发的时候了。可变长参数为日常开发提供了便利,增加了可读性,我们可以随心所欲地传入不等量的参数而不需要将他们装日一个Array中再拿出来解析(其实在oc中还是需要逐个拿出来解析的)。

首先我们看看在oc中是怎样运用的

- (void)appendToMyTail:(NSString *)values, ...NS_REQUIRES_NIL_TERMINATION {
    if (nil != values) {
        self = [self stringByAppendingString:values];
        NSString *tempStr;
        va_list varList;
        va_start(varList, values);
        while (tempStr = va_arg(varList, NSString *)) { //以NSString内存大小为偏移量取下一个值。
            self = [self stringByAppendingString:tempStr];
        }
        va_end(varList);
    } 
}

//invoke
NSString *str = @"";
[str appendToMyTail:@"my", @"name", @"is", @"Chiling", nil];
//str  "my name is Chiling"

在oc中使用可变参数,需要使用C函数,以可变参数的第一个参数的内存地址为起始,并以参数类型的内存大小为偏移量一个一个往下读取。
It is not possible with C varargs to know the number and types of the arguments. The called function has to somehow know the types and when to stop. 方法实现不能智能识别参数的类型与参数的数量而需要人工定义规则,实在让人有点喘不过气来。

在Swift中使用可变参数,我们只需要在参数的类型后面添加...即可,例如下面这是一个接收不定数量个value和一个分隔separator的函数

func appendToMyTail(values: String..., separator: String) {
    for str in values {
        //do something
    }
}

//invoke
var body = @""
body.appendToMyTail("my", "name", "is", "Chiling", separator: "-")

相比下,在swift中的可变参封装得让人舒服,没有位置的限制,方法实现里拿的就是一个帮你装好的集合,那么一个方法中可变参的个数是否也没有限制呢,这个却不是这样的,一个方法中可变参的个数依然是限制在一个。如果需要使用可变参数实现一些更智能的业务,那么就需要另外传入一些辅助标识符之类。在这里就不发挥想象了。