热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Swift2.2的新特性

导读:本文来自SwiftGG翻译组,作者@walkingway基于苹果Swift官方博客中TedKremenek所撰写的“Swift2.2Released!”文章进行了关于Swift2.

导读:本文来自SwiftGG翻译组,作者@walkingway基于苹果Swift官方博客中Ted Kremenek所撰写的“Swift 2.2 Released!”文章进行了关于Swift 2.2新特性的介绍翻译以及小试牛刀,Swift 2.2虽然是个小版本,但也有许多 得开发者注意的内容。

Swift 2.2 随着 iOS 9.3 一同闪亮登场,相较于 Swift 2.1, 2.2 版本做出了许多调整,从其调整方向上我们也能一窥 Swift 3.0 的影子,以下内容主要来自于苹果 Swift 官方 Blog,接下来就让我们进入正题,一睹 Swift 2.2 的庐山真面目:

允许更多的关键字来做参数标签

SE-0001: Allow (most) keywords as argument labels

参数标签是 Swift 中非常 cool 的一个特性,我们可以这么写:

fori in1.stride(to: 9, by: 2) { print(i) }

这个函数很简单,由 1 开始,每次加 2,返回一系列的 ,最后的结果要小于 9:

1 3 5 7

上面的函数如果参数前没有 to 或 by 标签,即 stride(9, 2) 那么代码将失去自解释性,别人也很难猜到这些参数的实际用途。

又假设我们要获取集合中某个 对应的索引,可以声明如下方法:

indexOf(value, in: collection)

但是注意在 Swift 2.2 之前的版本,上面这种写法 Xcode 会报错,因为 in 是一个关键字,想要使用这些关键字必须加上单引号:

indexOf(value, `in`: collection)

以前我们定义新的 API 的时候,通常也要避免与这些关键字撞车,比如用 within 代替 in。在我们导入 Objective-C APIs 的时候通常会碰到这些问题:

event.touchesMatching([.Began, .Moved], `in`: view) NSXPCInterface(`protocol`: SomeProtocolType.Protocol)

而在 Swift 2.2,我们开放了除 inout, var 和 let 以外所有的关键字,现在他们都可以作为参数 label 来使用了(而不用加单引号)关于语法的影响主要注意以下三方面:

1、函数调用中的关键字可以随意使用了,不会产生什么歧义,因为方法调用时 “:” 总是伴随着参数标签出现。

2、函数/子类化/初始化 声明:除 inout, var 和 let 这三个关键字之外,使用其他关键字没有什么歧义,因为这些关键字后面总是跟随着 ‘:’ 或 ‘_’ 比如:

func touchesMatching(phase: NSTouchPhase, inview: NSView?)- Set NSTouch

假如你想在函数声明中使用 inout, var 和 let 做为参数名的话,还是要加单引号:

func addParameter(name: String, `inout`: Bool)

3、如果在函数类型中这三个关键字(inout,var,let)出现的话,是不需要加单引号的,这是因为在这种情况下参数名后总是跟着 ‘:’

(NSTouchPhase, in: NSView?)- Set NSTouch (String, inout: Bool)- Void

元组对象可以进行比较操作了

SE-0015: Tuple comparison operators

元组是以逗号分割的 列表:

letdeveloper = ( Numbbbbb , Shanks ) letdesigner = ( Cee , Sai )

以前想要比较两个元组,我们需要自己重载操作符

func == (t1: (T, T), t2: (T, T))- Bool { returnt1.0== t2.0 t1.1== t2.1}

抛开每次都要写这一坨无趣的代码不说,而且只能比较包含两个元素的元组。不过在 Swift 2.2 中,我们可以直接比较两个元组了:

letdeveloper = ( Numbbbbb , Shanks ) letdesigner = ( Cee , Sai ) ifdeveloper == designer { print( Matching tuples! ) } else{ print( Non-matching tuples! ) }

Swift 2.2 允许不超过 6 个元素的元组之间进行比较,限制元组的元素个数主要有两个原因:

每一次比较都需要在基本库中添加额外的代码;元组的元素过多并不是一种好的编程风 ,考虑用结构体代替。

可以尝试下面两个元组比较:

letdeveloper = ( Numbbbbb , 23) letdesigner = ( Cee , Sai )

不出意外地报错了:

我们重点关注下结尾的部分:

note: overloads for '=='exist with these partially matching parameter lists: ...... ((A, B), (A, B)), ((A, B, C), (A, B, C)), ((A, B, C, D), (A, B, C, D)), ((A, B, C, D, E), (A, B, C, D, E)), ((A, B, C, D, E, F), (A, B, C, D, E, F))

Swift 内部函数确实逐字比较了元组的元素,直到 (A, B, C, D, E, F),没有超过 6 个元素。

为 AnySequence.init 增加约束条件

SE-0014: Constraining AnySequence.init

AnySequence 表示一个无类型的序列,他遵循 SequenceType 协议,而该协议拥有一个关联类型 associatedtype SubSequence ,而有时候我们需要 SubSequence 也要满足 SequenceType 协议。

假如我们有一个 _SequenceBox:

internal class _SequenceBox S : SequenceType : _AnySequenceBox S.Generator.Element { ...}

为了确保 SubSequence 满足 SequenceType,要做如下限定:

internal class _SequenceBox S : SequenceType where S.SubSequence: SequenceType, S.SubSequence.Generator.Element== S.Generator.Element, S.SubSequence.SubSequence== S.SubSequence : _AnySequenceBox S.Generator.Element { ... }

反过来,他也会影响 AnySequence.init 做一些限定:

修改前的 AnySequence.init:

public struct AnySequence Element : SequenceType { public init S: SequenceType where S.Generator.Element == Element (_ base: S) { ...} }

修改后的 AnySequence.init:

public struct AnySequence Element : SequenceType { public init S: SequenceType where S.Generator.Element == Element, S.SubSequence : SequenceType, S.SubSequence.Generator.Element == Element, S.SubSequence.SubSequence == S.SubSequence (_ base: S) { ...}
}

事实上,这些约束应该被应用到 SequenceType 协议自身上(尽管就目前来看是不太可能了),同我们预期的那样每个 SequenceType 实现都已经自我满足。

在声明相关类型时用 associatedtype 来替换 typealias

SE-0011: Replace typealias keyword with associatedtype for associated type declarations

在 Swift 2.2 以前的版本中关键字 typealias 可以用来声明两种类型:

类型别名(为已存在的类型起一个别名)关联类型(作为占位符类型成为协议的一部分)

以上两种声明应该使用不同的关键字,为此我们为关联类型准备了新的关键字 associatedtype,因此在 Swift 2.2 中 typealias 只能用做类型别名的声明,所以协议中使用的关联类型只能用 associatedtype,如果用了 typealias 就会报错:

protocol Prot { associatedtype Container : SequenceType typealias Element = Container.Generator.Element // error: cannot declaretypealias inside protocol, use protocol extension instead }

应将 typealias 移到 extension 中去

protocol Prot { associatedtype Container : SequenceType } extension Prot { typealias Element = Container.Generator.Element}

命名函数时带上参数标签

SE-0021: Naming Functions with Argument Labels

因为在 Swift 中,函数是一等公民,所以函数可以赋 给变量,当做普通 传递。为此我们需要一个函数类型来对该变量做限定。通常我们会使用函数名作为主要类型部分,但是有许多基本名字相同的函数,仅仅是参数或参数标签不同而已,比如 UIView:

extension UIView{ func insertSubview(view: UIView, at index: Int) func insertSubview(view: UIView, aboveSubview siblingSubview: UIView) func insertSubview(view: UIView, belowSubview siblingSubview: UIView) }

我们调用时也是通过参数标签来区分不同的方法:

someView.insertSubview(view, at: 3) someView.insertSubview(view, aboveSubview: otherView) someView.insertSubview(view, belowSubview: otherView)

但是,当我们创建一个函数的引用时,就会产生一个歧义,即无法确定调用的是 UIView 的哪个方法。

letfn = someView.insertSubview // ambiguous: could beany ofthe three methods

我们可以使用类型注解来消除歧义:

letfn: (UIView, Int) = someView.insertSubview // ok: uses insertSubview(_:at:)letfn: (UIView, UIView) = someView.insertSubview // error: still ambiguous!

但是上面的代码后者因为 (UIView, UIView) 存在两个方法(aboveSubview 和 belowSubview),所以还是存在歧义,只能用闭包的方式来指名传递的方法:

letfn: (UIView, UIView) = { view, otherView in button.insertSubview(view, aboveSubview: otherView) }

这样做法太乏味了,Swift 2.2 现在允许我们将函数命名为:函数名 参数标签的组合来消除歧义:

letfn= someView.insertSubview(_:at:) letfn1 = someView.insertSubview(_:aboveSubview:)

同样的语法也可以用做初始化的引用:

letbuttOnFactory= UIButton.init(type:)

为指定的方法生成一个 Objective-C 选择器:

letgetter = Selector(NSDictionary.insertSubview(_:aboveSubview:)) // produces insertSubview:aboveSubview:.

引用 Objective-C 的选择器方法

SE-0022: Referencing the Objective-C selector of a method

在 Swift 2,Objective-C selectors 通常会根据其字面 写成字符串常量,比如 “insertSubview:aboveSubview:” 这样比较容易出错,例如下面的:

navigationItem.rightBarButtOnItem= UIBarButtonItem(title: Tap! , style: .Plain, target: self, action: buttonTaped )

如果你 神够好,会发现我把 buttonTapped 写成了 buttonTaped,但 Xcode 也不会给我报错。这一切在 Swift 2.2 终于得到解决,字符串作为 selector 被 deprecated 了,今后该这么写 #selector(buttonTapped),这样发生拼写错误,也能及时得到编译器的提醒。

即使在纯 Swift 环境中(完全与 Objective-C 完全无关),我们也可以通过 #selector(Swift 方法名) 的方式来实现 Swift 的 selector。

control.sendAction(#selector(MyApplication.doSomething), to: target, forEvent: event) extensionMyApplication { @objc(jumpUpAndDown:) func doSomething(sender: AnyObject?) { … }}

创建一个 Selector 的引用:

letsel =#selector(UIView.insertSubview(_:at:)) // produces the Selector insertSubview:atIndex:

编译期 Swift 的版本检查

SE-0020: Swift Language Version Build Configuration

在大部分时候,随着 Swift 版本更新语法也会有较大调整,但是第三方类库的维护者们希望他们的库能够同时兼容不同版本的 Swift,目前可行的办法是同时维护多个分支来支持不同版本的语言。

Swift 2.2 提供了新的选项使你将同一个版本的 Swift 代码集中在同一个文件中,而编译器会在编译时选择具体的 Swift 版本来执行

#ifswift( =2.2)print( Running Swift 2.2 or later ) #elseprint( Running Swift 2.1 or earlier ) #endif

类 于现存的 #if os() 构建选项,这个选项决定了编译器随后生成的代码,如果你使用的是 Swift 2.2,那么第二个 print() 将不会被看到。

其它一些特性

苹果 Swift 官方 Blog 没有提到的 Swift 2.2 一些新特性:

和 – 将被取消

Swift 2.2 正式将 和 – deprecates 掉了,意味着虽然在 Swift 2.2 版本还能工作,但编译器会给你一个警告。但在 3.0 版本会被完全移除。

你可以使用 = 1 和 -= 1 来替代,至于为什么要将其移除,有这么几个解释:

写 并不比 =1 能节省多少时间 对学 Swift 没有任何帮助, = 至少可读性更好传统 C styel for 循环中的 – 也被 deprecated 了

传统 C 风 的 for 循环被干掉了

也就是说下面这种写法在 2.2 的版本被 deprecates 了

forvar i= 1; i = 10; i = 1{ print( \(i) SwiftGG awesome ) }

以后要这么写了:

fori in1...10{ print( \(i) SwiftGG awesome ) }

如果想要创建一个由大到小的范围,你按照下面的写法编译或许没问题,但运行时会崩溃:

fori in10...1{ print( \(i) SwiftGG awesome ) }

应当这么写:

fori in(1...10).reverse() { print( \(i) SwiftGG awesome ) }

另一种选择是使用标准的快速枚举来遍历数组

vararray= Array(1...10) fornumber in array{ print( \(number) green bottles ) }

数组和其他一些 slice types 现在有 removeFirst() 方法了

Swift 2.2 终于为我们带来了 removeFirst() 方法,该方法将从数组中移除第一个元素,然后返回给我们,可以试验一下

vararray= Array(1...5) array.removeFirst() fornumber in array{ print( the \(number) bird ) }

使用 removeLast() 时要注意,如果是空数组,会崩溃,因此可以用 popLast() 来替代,该方法会处理空数组的情形(返回 nil)。

元组 splat 语法被废除了

我们可以用下面的方式定义一个函数,在 Swift 2.2 之前可以有两种方式调用。

func foo(a : Int, b : Int) {}

第一种我们经常使用,为函数的每个参数都传递相对应的

foo(42, b : 17)

或者我们可以利用一个大部分开发者都不那么熟悉的特性(tuple splat)来调用

letx = (1, b: 2) foo(x)

后者这种语法糖实在没什么实际意义,在 Swift 2.2 被 deprecated,将在未来的版本移除。

var 参数被废除了

var 参数提供的益处微乎其微,而且容易让人与 inout 混淆,因此在 Swift 2.2 中被移除了。

举个例子 sayHello() 函数使用了 var 参数:

func sayHello(var name: String, repeatrepeatCount: Int) { name= name.uppercaseString for_ in0.. repeatCount { print(name) } } sayHello( numbbbbb , repeat: 5)

结果是 NUMBBBBB 将会被打印 5 遍,这是因为参数 name 经 var 修饰后成为变量,然后执行 uppercaseString 方法转换为大写,如果没有 var 关键字,name 是常量,执行 uppercaseString 会失败。

var 和 inout 之间的差异非常微妙:

使用 var,让你可以在函数内部修改参数使用 inout,甚至可以让你的改变延续到函数结束后

我们可以在 Swift 2.2 中这么写:

func sayHello(name: String, repeatrepeatCount: Int) { let upperName = name.uppercaseString for_ in0.. repeatCount { print(upperName) } } sayHello( numbbbbb , repeat: 5)

重命名 debug 标识符:#line, #function, #file

在 Swift 2.1 和之前的版本,使用 FILE, LINE, COLUMN, 和 FUNCTION标识符,在编译时会被替换为文件名、行号、列号和函数名。

而在 Swift 2.2 这些旧的标识符被更新为 #file, #line, #column 和 #function,如果你之前使用过 Swift 2.0 的 #available 来检查 iOS 版本,正如官方所说 # 意味这编译器这里要执行替换逻辑。

下面在 printGreeting() 函数中演示了新旧两种 debug 标识符:

func sayHello(name: String, repeatrepeatCount: Int) { // old - deprecated! print( This is on line \(__LINE__) of \(__FUNCTION__) ) // new - shiny! print( This is on line \(#line) of \(#function) ) let upperName = name.uppercaseString for_ in0.. repeatCount
{ print(upperName) } } sayHello( numbbbbb , repeat: 5)


推荐阅读
  • 本文探讨了在iOS平台上开发BLE(蓝牙低功耗)应用程序时遇到的挑战,特别是如何实现应用在后台模式下仍能持续扫描并连接蓝牙设备。文章提供了具体的配置方法和常见的问题解决方案。 ... [详细]
  • 实现‘点击恢复’功能 - Tap-to-Resume Feature in SpriteKit
    了解如何在应用程序从非活动状态返回时,在SpriteKit游戏中添加一个‘点击恢复’的文字提示。 ... [详细]
  • 本文介绍了iOS应用开发的主要框架,包括Foundation、UIKit、CoreData及CoreGraphics等,并探讨了开发iOS应用所需的硬件和软件环境,以及推荐的编程语言。 ... [详细]
  • 本文详细介绍了JSP(Java Server Pages)的九大内置对象及其功能,探讨了JSP与Servlet之间的关系及差异,并提供了实际编码示例。此外,还讨论了网页开发中常见的编码转换问题以及JSP的两种页面跳转方式。 ... [详细]
  • 在使用 iOS 应用时,遇到网络请求错误是常见的问题。本文将探讨两种常见的错误代码 -1003 和 -1001,并提供详细的解释和解决方案。 ... [详细]
  • 在Swift项目中集成Objective-C类或第三方框架的方法
    本文通过实例讲解如何在Swift项目中引入并使用Objective-C编写的ProgressHUD库。首先需要在项目中添加库文件,并设置Objective-C桥接头文件以实现语言间的互操作性。 ... [详细]
  • 本文将详细介绍多个流行的 Android 视频处理开源框架,包括 ijkplayer、FFmpeg、Vitamio、ExoPlayer 等。每个框架都有其独特的优势和应用场景,帮助开发者更高效地进行视频处理和播放。 ... [详细]
  • Python 工具推荐 | PyHubWeekly 第二十一期:提升命令行体验的五大工具
    本期 PyHubWeekly 为大家精选了 GitHub 上五个优秀的 Python 工具,涵盖金融数据可视化、终端美化、国际化支持、图像增强和远程 Shell 环境配置。欢迎关注并参与项目。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • 本文详细介绍了虚拟专用网(Virtual Private Network, VPN)的概念及其通过公共网络(如互联网)构建临时且安全连接的技术特点。文章探讨了不同类型的隧道协议,包括第二层和第三层隧道协议,并提供了针对IPSec、GRE以及MPLS VPN的具体配置指导。 ... [详细]
  • 本文介绍了如何在iOS应用中自定义导航栏按钮,包括使用普通按钮和图片生成导航条专用按钮的方法。同时,探讨了在不同版本的iOS系统中实现多按钮布局的技术方案。 ... [详细]
  • java文本编辑器,java文本编辑器设计思路
    java文本编辑器,java文本编辑器设计思路 ... [详细]
  • 本文深入探讨了 Delphi 中类对象成员的核心概念,包括 System 单元的基础知识、TObject 类的定义及其方法、TClass 的作用以及对象的消息处理机制。文章不仅解释了这些概念的基本原理,还提供了丰富的补充和专业解答,帮助读者全面理解 Delphi 的面向对象编程。 ... [详细]
  • 本文介绍了如何使用JFreeChart库创建一个美观且功能丰富的环形图。通过设置主题、字体和颜色等属性,可以生成符合特定需求的图表。 ... [详细]
  • 本文详细介绍了ASP.NET缓存的基本概念和使用方法,包括输出缓存、数据缓存及其高级特性,如缓存依赖、自定义缓存和缓存配置文件等。通过合理利用这些缓存技术,可以显著提升Web应用程序的性能。 ... [详细]
author-avatar
云彩熨衣裳
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有