今天我们来谈一谈Swift中的操作 符重载,这一功能非常实用,但是也相当有风险。正所谓“能力越大责任越大”,这句话用来形容操作符重载最合适不过了。它可以令你的代码更加简洁,也可以让 一个函数调用变得又臭又长。而对于那些没怎么读过你的代码的人来说,操作符的使用同时也会让代码的可读性大打折扣。


慎引入,按需使用。比如在连接两个字串的时候你就可以通过重载加法来实现。甚至于你仅在屏幕上输入一个加号,就能响应一个网络链接、播放一段音乐或者完成
你能实现的其他任何功能。然而过于复杂的功能对编码来说简直就是灾难,合理的做法就是仅在必要时重载操作符,不做任何多余的事。如果你非要做点奇怪的事,
那就为网络响应这样的功能取一个明显又合适的函数名。

无论你何时想采用运算符重载,你都需要考虑一下是否值得重载它而不是用一个函数调用。函数调用或许颇费周折,但更能确切的表达它的功能(如果函数名恰到好处的话)。假如你要频繁做类似于加减乘除赋值比较的操作,那么运算符重载的确是个很合适的解决方案。

运算符重载

在Swift里面你应该就像平时使用运算符那样来使用重载算符。虽然仍然有些不足的地方,不过下面的这个例子足够说明重载运算符的使用了。这段代码计算的是两个NSDate对象间的差值,使用了“laterDate–initialDate” 的形式:

func - (left: NSDate, right: NSDate) -> NSDateComponents
{
    let mostUnits: NSCalendarUnit = .YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit
    
    let components = NSCalendar.currentCalendar().components(mostUnits, fromDate: right, toDate: left, options:nil)
    
    return components
}

在这个函数中,第一行看上去很多,然而mostUnits表示的不过是我们所关心的日期单元(NSCalendarUnit)的清 单,在这里我选择关注的是年、月、日、时、分、秒。值得注意的是这里并非只有这些日期单元的Flag,可选的还有很多,如果我想知道两个日期之间相隔的周 数我一样可以用别的Flag来替换掉上面这些,使用这些不同Flag可以满足我们对日期计算的大部分需求。往下的一行中,使用了当前时间 NSCalendar.currentCalendar()的components()方法创建了一个NSDateComponents对象,之后在方法 最后返回了这个对象components.

在两个值之间的运算成为中缀运算,运算重载还有后缀运算和前缀运算两种。比如++i和i++分别就是前缀和后缀运算。

如你所见,Swift中所谓重载不过是挂羊头卖狗肉,实际上也只是一个函数调用而已,只不过函数名变成了符号。我们指定了表达式中左右参数的类型,并且还指定了计算结果返回的数据类型必须是一个NSDateComponents对象,下面是代码:

let initialDate = NSDate()
let components = NSDateComponents()
components.year = 1
components.month = 5
components.minute = 42
let laterDate = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: initialDate, options: nil)!
//Test the overloaded operator
let difference = laterDate - initialDate
//Results
println(difference.year)    //Outputs: "1"
println(difference.month)   //Outputs: "5"
println(difference.day)     //Outputs: "0"
println(difference.hour)    //Outputs: "0"
println(difference.minute)  //Outputs: "42"
println(difference.second)  //Outputs: "0"

这里有一些设置,不过大部分很好理解。我们先 初始化一个日期数据,它的值就是这个NSDate对象的创建日期,另外还有一个NSDateComponents对象包含了它自己的年月和分钟的属性值。 之后使用NSCalendar的dateByAddingComponents方法设置laterDate为1年5个月又42分钟之后的时间。然后测试重 载后的新功能“-”操作符,你会看到最后通过减法输出的结果刚好是我们设置的时间差。

我认为这样的重载还是有点用的,我们可以像平时使用数 学减法那样来计算。然而,这里隐藏某些假定的条件:首先我已经提到过关于NSCalendarUnit的Flag只是使用了一些而非全部;其次,我在重载 函数里明确使用了NSCalendar的当期日期(currentCalendar)在这里没有显示。最后它把计算结果difference的属性值都设 为了0,但我们其实并没有想使用其他的属性。不过就我所知,某些人也许会想到封装所有的属性来解决。

如果使用了普通的函数调用来代替重载运 算符,你可以很清楚使用了哪种格式的日期,选中哪些Flag,以及在diffrence里面可以发现哪些属性被设置过。这就是为什么我说重载运算符只是 “有点用”。原因就是它对代码的读者而言,想要知道函数中究竟隐藏了哪些假定条件的话只能扒开重载运算符的源码看了。

Equatable协议

Swift中的重载运算符里面还是有一些比较不错的地方。你可以通过重载"=="算符来实现对自定义类的比较。假如你想实现这一功能你就需要使用Equatable协议,Equatable协议主要应用于泛型编程(有关Swift的泛型编程可以参考这里)。如果你按照Equatable协议重载了"=="操作符,那么你无需再重载"!="操作符(因为“!=”的重载就是"=="的逻辑否)。

重载"=="的方式与其他的中缀运算的重载方式一致,就像我们在上面所提到的"-"重载一样:

//Globally scoped operator function
func == (left: Temperature, right: Temperature) -> Bool
{
    if left.C == right.C
    {
        return true
    }
    else
    {
        return false
    }
}
//Custom type itself
struct Temperature:Equatable
{
    let C: Double
}
//Test
let tempOne = Temperature(C: 15)
let tempTwo = Temperature(C: 35)
let tempThree = Temperature(C: 15)
let OneTwoEquality = (tempOne == tempTwo)       //Stores:   false
let OneThreeEquality = (tempOne == tempThree)   //Stores:   true

现在我们可以使用“==”重载运算来比较两个Temperature实例了。如果要重载一个算符那么它必须要有一个对应的全局函数,甚至定义在类型之外。

Swift 中关于运算重载还有一个很不错的地方就是Comparable协议。如同遵照Equatable协议一样你在照着Comparable协议实现的时候你需 要重载一个“<”方法。另外如果你重载了"=="和">"运算符之后编译器会为你计算出其它几个运算符"!="、"<"、"& lt;="和">="的判定。可以看这里的一个例子,它同样也是Equatable协议的例子。

总结

本文中所有代码的测试环境均为Xcode 6.0.1。

前面我讨论了一下NSDate的减法,是因为它在被调用时隐藏了不少的假定条件。在这里提醒一下:当你在考虑编码中是否要实现重载运算符的时候可以想想这个示例。如果你想在代码里一直使用相同的unitFlag来重载实现日期的减法,你需要多写一些代码来考虑多种情况。

如果你想重载内建类型以及不想修改(或者无法修改)的类型的运算符重载,你可以在扩展里面添加这个重载函数。且这个重载函数需要为全局作用域,并放在你随便哪个扩展存放的文件里面。

有一些运算符是不能重载的,最为明显的就是赋值运算"="(一个等号)。就算你重载了,想一想赋值运算的使用频率,我不会对产生的错误有任何兴趣的。这里有Tammo Freese同学的一篇文章,你可以了解一下哪些运算符是不可以被重载的。

再议Swift操作符重载的更多相关文章

  1. c++ 操作符重载和友元

    操作符重载(operator overloading)是C++中的一种多态,C++允许用户自定义函数名称相同但参数列表不同的函数,这被称为函数重载或函数多态.操作符重载函数的格式一般为: operat ...

  2. [置顶] operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)

    operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...

  3. C++基础学习笔记----第十三课(操作符重载-下)

    本节主要讲使用成员函数重载操作符,包括[],=,(),->四种操作符的重载以及&&和||的问题. 类的成员函数进行操作符重载 基本概念 类的成员函数也可以进行操作符的重载.类的普 ...

  4. C++ operator overload -- 操作符重载

    C++ operator overload -- 操作符重载 2011-12-13 14:18:29 分类: C/C++ 操作符重载有两种方式,一是以成员函数方式重载,另一种是全局函数. 先看例子 # ...

  5. C#构造函数、操作符重载以及自定义类型转换

    构造器 构造器(构造函数)是将类型的实例初始化的特殊方法.构造器可分为实例构造器和类型构造器,本节将详细介绍有关内容. 实例构造器 顾名思义,实例构造器的作用就是对类型的实例进行初始化.如果类没有显示 ...

  6. 15.C++-操作符重载

    首先回忆下以前学的函数重载 函数重载 函数重载的本质为相互独立的不同函数 通过函数名和函数参数来确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的函数重载 ...

  7. 用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- 操作符重载和隐式类型转换

    C#中,某些类型会定义隐式类型转换和操作符重载.Unity中,有些对象也定义了隐式类型转换和操作符重载.典型情况有:UnityEngine.Object.UnityEngine.Object的销毁是调 ...

  8. 5.1 C++基本操作符重载

    参考:http://www.weixueyuan.net/view/6379.html 总结: 操作符重载指的是将C++提供的操作符进行重新定义,使之满足我们所需要的一些功能. 长度运算符“sizeo ...

  9. 15.C++-操作符重载、并实现复数类

    首先回忆下以前学的函数重载 函数重载 函数重载的本质为相互独立的不同函数 通过函数名和函数参数来确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的函数重载 ...

随机推荐

  1. go 学习笔记 - sublime text 环境配置

    园里已经有了一篇相当不错的配置说明文章,只是现在gosublime不再支持2.x.文章里的操作在sublimetext3 里一样可以使用 文章地址 : http://www.cnblogs.com/s ...

  2. 转:Xshell显示找不到匹配的outgoing encryption算法怎么办

    原文出处:http://www.xshellcn.com/xsh_column/suanfa-bpp.html 由用户反应在使用xshell和xftp连接debian 7时出现找不到匹配的outgoi ...

  3. 第四章 Linux环境

    程序参数 main函数声明: int main(int  argc, char  *argv[]) 其中argc是接受到的参数个数,argv是存储参数的字符串数组. 或者声明为: main() 这样也 ...

  4. js执行环境深入研究

    js 声明函数是创建函数对象的过程,当创建函数对象时,函数对象的[[scope]] =连当前执行环境对象的作用域(栈顶执行环境--当执行函数时,js会将该函数的执行环境对象入栈) 当为全局函数时,如: ...

  5. C#获取本机IP方法,获取本机局域网IP地址方法

    1. private void GetIP() { string hostName = Dns.GetHostName();//本机名 //System.Net.IPAddress[] address ...

  6. 为 IIS 7.0 配置 <system.webServer>

    Web.config 文件中的 system.webServer 节用于指定适用于 Web 应用程序的 IIS 7.0 设置.system.WebServer 是 configuration 节的子级 ...

  7. c# 面相对象4-多态性

    一.定义: 多态是面向对象程序设计的又一个特性.在面向过程的程序设计中,主要工作是编写一个个的过程或函数,这些过程和函数不能重名.例如在一个应用中,需要对数值型数据进行排序,还需要对字符型数据进行排序 ...

  8. 如何合并相同数据并转置(mysql)实现

    上次参加天猫大数据竞赛 是预测用户会买哪些牌子给用户推荐 拥有的字段 user_id,brand_id 最后要转成如下格式,比如用户1,要给他推荐2,3,4号品牌 原来的数据是 user_id,bra ...

  9. 解决linux top命令提示的unknown terminal type的问题

    [root@localhost bin]# top 'xterm-256color': unknown terminal type. 在网上搜索了解决方法如下: 解决办法: 1.临时办法,下次启动失效 ...

  10. 【Android & iOS】应用升级实现

    在移动应用中,都会有的一个功能就是应用版本升级,怎么实现这个功能呢? 基本的思路就是:对比当前使用的应用版本和最新的版本号,如果版本号不一致,就可以提示用户升级啦. Android中,可以通过一下方式 ...