[翻译]Swift编程语言—— 扩展_9123456789yongyingyufanyi-程序员宅基地

技术标签: 编程语言  Swift  

扩展

扩展就是给存在的类、结构体或者枚举类型添加新的功能。这包括了对不能够访问源代码的类型进行扩展的能力(这被称作逆向建模)。扩展和OC中的categories 类似。(和OC中的categories 不同的是,Swfit的扩展不可以有名字。)

Swift中的扩展可以做以下事情:
添加计算属性和计算静态属性;
定义实例方法和类型方法;
提供新的构造方法;
定义下标;
定义和使用新的潜逃类型;
让被扩展的类型遵循一个协议;

NOTE
扩展可以给一个类型添加新功能,但不能重写已经存在的功能。

扩展语法

使用extension关键字定义扩展:

extension​ ​SomeType​ {
​    ​// new functionality to add to SomeType goes here
​}

一个扩展可以扩展一个存在的类型,使其遵循一个或多个协议。当这种情况时,像定义一个类或者结构体一样书写协议的名字(译者注:意思是像定义一个类或者结构体的语法一样前面写定义的名字后面跟类型的名字,二者用冒号分割):

extension​ ​SomeType​: ​SomeProtocol​, ​AnotherProtocol​ {
​    ​// implementation of protocol requirements goes here
​}

这种方式添加对协议的遵循,详见 使用扩展添加对协议的遵循(Adding Portocol Conformance with an Extension) 一节。

NOTE
如果定义了一个扩展给存在的类型添加了功能,新的功能对于所有这个类型的实例都有效,尽管可能实例在扩展定义前就被创建了。

计算属性

扩展可以给存在的类添加实例计算属性和类型计算属性。这个例子给Swift内嵌的Double类型添加了五个实例计算属性,来处理不同的距离单位:

​extension​ ​Double​ {
​    ​var​ ​km​: ​Double​ { ​return​ ​self​ * ​1_000.0​ }
​    ​var​ ​m​: ​Double​ { ​return​ ​self​ }
​    ​var​ ​cm​: ​Double​ { ​return​ ​self​ / ​100.0​ }
​    ​var​ ​mm​: ​Double​ { ​return​ ​self​ / ​1_000.0​ }
​    ​var​ ​ft​: ​Double​ { ​return​ ​self​ / ​3.28084​ }
​}
​let​ ​oneInch​ = ​25.4​.​mm
​println​(​"One inch is ​\(​oneInch​)​ meters"​)
​// prints "One inch is 0.0254 meters"
​let​ ​threeFeet​ = ​3​.​ft
​println​(​"Three feet is ​\(​threeFeet​)​ meters"​)
​// prints "Three feet is 0.914399970739201 meters"

这些计算属性表示了一个Double数值将会被作为特定的长度单位被转换。尽管他们是作为计算属性被实现的,但是这些属性名字可以通过采用一个点号附加在一个浮点字面值上,这是一种对那个字面值进行距离表示转换的方式。

这个例子中,一个Double值1.0被当作“1米。这就是为什么m计算属性返回self——表达式1.m被当作一个Double值1.0来计算。

其他的单位需要对以米单位测量的数值进行转换。一千米和1000米是一样的,所以km计算属性会将值乘以1000,做到将数值转换为以米为单位的数字表达式。类似的,一米是3.28024英尺,所以ft计算属性将数值除以3.28024,转换为米制。

这些属性是制度的计算属性,为了简洁,可以不通过get关键字访问。这些属性的返回值都是Double类型的,所以适用于Double类型的计算:

​let​ ​aMarathon​ = ​42​.​km​ + ​195​.​m
​println​(​"A marathon is ​\(​aMarathon​)​ meters long"​)
​// prints "A marathon is 42195.0 meters long"

NOTE
扩展可以添加新的计算属性,但是不能添加存储属性和给已经存在的属性添加观察者。

构造方法

扩展可以给已经存在的类型添加新的构造方法。这样可以扩展构造方法参数的类型,或者提供类型原始定义中并不存在的额外的构造方法选项。

扩展可以给类添加新的方便构造方法,但是不能给类添加新的指定构造方法或者析构方法。指定构造方法和析构方法必须在类的初始定义中提供。

NOTE
如果使用扩展给值类型添加一个构造方法,同时,这个值类型给所有存储值都提供了默认值并且没有定义任何的定制构造方法,那么可以在扩展的构造方法中调用默认的构造方法和成员初始化构造方法。

如果值类型的原始定义中有定义构造方法,就不能像上述一样做。这在 值类型构造方法的委托 (Initializer Delegation for Value Types)有描述。

下面的例子定义了一个Rect结构体表现一个几何学上的矩形。这个例子同时定义了两个支撑的结构体叫做Size和Point,它们两个给它们的属性提供的默认值都是0.0:

​struct​ ​Size​ {
​    ​var​ ​width​ = ​0.0​, ​height​ = ​0.0
​}
​struct​ ​Point​ {
​    ​var​ ​x​ = ​0.0​, ​y​ = ​0.0
​}
​struct​ ​Rect​ {
​    ​var​ ​origin​ = ​Point​()
​    ​var​ ​size​ = ​Size​()
​}

因为Rect结构体给它所有的属性提供了了默认值,自动地它会得到一个默认的构造方法和一个成员构造方法,就像 默认构造方法(Default Initializers)一节描述的一样。这些初始化方法可以用来创建一个新的Rect实例:

​let​ ​defaultRect​ = ​Rect​()
​let​ ​memberwiseRect​ = ​Rect​(​origin​: ​Point​(​x​: ​2.0​, ​y​: ​2.0​),
​    ​size​: ​Size​(​width​: ​5.0​, ​height​: ​5.0​))

可以对Rect结构体进行扩展,添加一个带指定中心点和尺寸的构造方法:

​extension​ ​Rect​ {
​    ​init​(​center​: ​Point​, ​size​: ​Size​) {
​        ​let​ ​originX​ = ​center​.​x​ - (​size​.​width​ / ​2​)
​        ​let​ ​originY​ = ​center​.​y​ - (​size​.​height​ / ​2​)
​        ​self​.​init​(​origin​: ​Point​(​x​: ​originX​, ​y​: ​originY​), ​size​: ​size​)
​    }
​}

这个新的构造方法一开始根据提供的center点和size值计算合适的初始点。然后调用了结构体的自动成员构造方法init(origin:size:),这个构造方法给对应的属性存储了新的origin和size值:

let​ ​centerRect​ = ​Rect​(​center​: ​Point​(​x​: ​4.0​, ​y​: ​4.0​),
​    ​size​: ​Size​(​width​: ​3.0​, ​height​: ​3.0​))
​// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)

NOTE

如果用扩展的方式添加了一个新的构造方法,仍然需要确保在构造方法完成前所有的实例被完全初始化了。

方法

扩展可以给存在的类型添加新的实例方法和类型方法。下面的例子给Int类型添加了一个叫做repettions的实例方法:

​extension​ ​Int​ {
​    ​func​ ​repetitions​(​task​: () -> ()) {
​        ​for​ ​i​ ​in​ ​0​..<​self​ {
​            ​task​()
​        }
​    }
​}

respetitions方法带了一个()->()类型的参数,参数是一个没有参数和返回值的方法。

扩展之后,可以对任何的整型数字调用repetitions方法,执行若干次参数中的方法:

​3​.​repetitions​({
​    ​println​(​"Hello!"​)
​})
​// Hello!
​// Hello!
​// Hello!

使用追踪闭包语法(trailing closure syntax)使得调用更加简明:

​3​.​repetitions​ {
​    ​println​(​"Goodbye!"​)
​}
​// Goodbye!
​// Goodbye!
​// Goodbye!

实例方法的变异

通过扩展添加的实力方法可以修改(或变异)实力本身。标记实例方法为mutating,结构体和枚举的方法可以修改self和自身的属性,就像来自最初的变异方法一样。

下面的例子添加了一个变异方法square给Swift的Int类型,它会就是那初始值的平方:

extension​ ​Int​ {
​    ​mutating​ ​func​ ​square​() {
​        ​self​ = ​self​ * ​self
​    }
​}
​var​ ​someInt​ = ​3
​someInt​.​square​()
​// someInt is now 9

下标

扩展可以给已有的类型添加新的下标。这个例子给Swfit的内嵌Int类型添加了一个整型下标。这个下标[n]返回从数字的右边数起第n位的数字:

123456789[0] returns 9

123456789[1] returns 8

……继续:

​extension​ ​Int​ {
​    ​subscript​(​var​ ​digitIndex​: ​Int​) -> ​Int​ {
​        ​var​ ​decimalBase​ = ​1
​        ​while​ ​digitIndex​ > ​0​ {
​            ​decimalBase​ *= ​10
​            --​digitIndex
​        }
​        ​return​ (​self​ / ​decimalBase​) % ​10
​    }
​}

​746381295​[​0​]
​// returns 5
​746381295​[​1​]
​// returns 9
​746381295​[​2​]
​// returns 2
​746381295​[​8​]
​// returns 7

如果Int值没有足够的数位,下标会返回0,就像从左侧不齐了0一样:

​746381295​[​9​]
​// returns 0, as if you had requested:
​0746381295​[​9​]

嵌套类型

扩展可以给已经存在的类、结构体和枚举类型添加新的嵌套类型:

​extension​ ​Int​ {
​    ​enum​ ​Kind​ {
​        ​case​ ​Negative​, ​Zero​, ​Positive
​    }
​    ​var​ ​kind​: ​Kind​ {
​        ​switch​ ​self​ {
​        ​case​ ​0​:
​            ​return​ .​Zero
​        ​case​ ​let​ ​x​ ​where​ ​x​ > ​0​:
​            ​return​ .​Positive
​        ​default​:
​            ​return​ .​Negative
​        }
​    }
​}

这个例子给Int添加了新的嵌套枚举。这个枚举叫做Kind,表现了特定整型的类型,整型数字无非就是正数、零和负数。

这个例子同时给Int添加了新的实例计算属性,叫做kind,它返回当前整型对应的Kind枚举值。

嵌套枚举可以在任何Int值上使用:

​func​ ​printIntegerKinds​(​numbers​: [​Int​]) {
​    ​for​ ​number​ ​in​ ​numbers​ {
​        ​switch​ ​number​.​kind​ {
​        ​case​ .​Negative​:
​            ​print​(​"- "​)
​        ​case​ .​Zero​:
​            ​print​(​"0 "​)
​        ​case​ .​Positive​:
​            ​print​(​"+ "​)
​        }
​    }
​    ​print​(​"\n"​)
​}
​printIntegerKinds​([​3​, ​19​, -​27​, ​0​, -​6​, ​0​, ​7​])
​// prints "+ + - 0 - 0 +"

printIntegerKinds这个函数,参数是一个Int型的数组,对其进行了迭代。对于每个数组中的整数成员,函数会考量其kind计算属性,打印一个对应的描述。

NOTE
已知number.kind是Int.Kind类型的。因此,所有的Int.Kind成员在switch语句中可以简写,比如.Negative是Int.Kind.Negative的简写。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/erwin2012/article/details/46531645

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签