在 Swift 中实现 NS_OPTIONS

从Xcode 4.5以后,我们在Objective-C中使用NS_ENUM和NS_OPTIONS来定义一个枚举,以替代C语言枚举的定义方式。其中NS_ENUM用于定义普通的枚举,NS_OPTIONS用于定义选项类型的枚举。

而到了Swift中,枚举增加了更多的特性。它可以包含原始类型(不再局限于整型)以及相关值。正是由于这些原因,枚举在Swift中得到了更广泛的应用。在Foundation中,Objective-C中的NS_ENUM类型的枚举,都会自动转换成Swift中enum,并且更加精炼。以Collection View的滚动方向为例,在Objective-C中,其定义如下:


1

2

3

4

typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {

UICollectionViewScrollDirectionVertical,

UICollectionViewScrollDirectionHorizontal

};

而在Swift中,其定义如下:


1

2

3

4

enum UICollectionViewScrollDirection : Int {

case Vertical

case Horizontal

}

精练多了吧,看着舒服多了,还能少码两个字。我们自己定义枚举时,也应该采用这种方式。

不过对于Objective-C中NS_OPTIONS类型的枚举,Swift中的实现似乎就没有那么美好了。

我们再来对比一下UICollectionViewScrollPosition的定义吧,在Objective-C中,其定义如下:


1

2

3

4

5

6

7

8

9

10

11

12

typedef NS_OPTIONS(NSUInteger, UICollectionViewScrollPosition) {

UICollectionViewScrollPositionNone                 = 0,

// The vertical positions are mutually exclusive to each other, but are bitwise or-able with the horizontal scroll positions.

// Combining positions from the same grouping (horizontal or vertical) will result in an NSInvalidArgumentException.

UICollectionViewScrollPositionTop                  = 1 << 0,

UICollectionViewScrollPositionCenteredVertically   = 1 << 1,

UICollectionViewScrollPositionBottom               = 1 << 2,

// Likewise, the horizontal positions are mutually exclusive to each other.

UICollectionViewScrollPositionLeft                 = 1 << 3,

UICollectionViewScrollPositionCenteredHorizontally = 1 << 4,

UICollectionViewScrollPositionRight                = 1 << 5

};

而在Swift 2.0中,其定义如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

struct UICollectionViewScrollPosition : OptionSetType {

init(rawValue: UInt)

static var None: UICollectionViewScrollPosition { get }

// The vertical positions are mutually exclusive to each other, but are bitwise or-able with the horizontal scroll positions.

// Combining positions from the same grouping (horizontal or vertical) will result in an NSInvalidArgumentException.

static var Top: UICollectionViewScrollPosition { get }

static var CenteredVertically: UICollectionViewScrollPosition { get }

static var Bottom: UICollectionViewScrollPosition { get }

// Likewise, the horizontal positions are mutually exclusive to each other.

static var Left: UICollectionViewScrollPosition { get }

static var CenteredHorizontally: UICollectionViewScrollPosition { get }

static var Right: UICollectionViewScrollPosition { get }

}

额,光看代码,不看实现,这也是化简为繁的节奏啊。

为什么要这样做呢?Mattt给了我们如下解释:

Well, the same integer bitmasking tricks in C don’t work for enumerated types in Swift. An enum represents a type with a closed set of valid options, without a built-in mechanism for representing a conjunction of options for that type. An enum could, ostensibly, define a case for all possible combinations of values, but for n > 3, the combinatorics make this approach untenable.

意思是Swift不支持C语言中枚举值的整型掩码操作的技巧。在Swift中,一个枚举可以表示一组有效选项的集合,但却没有办法支持这些选项的组合操作(“&”、”|”等)。理论上,一个枚举可以定义选项值的任意组合值,但对于n > 3这种操作,却无法有效的支持。

为了支持类NS_OPTIONS的枚举,Swift 2.0中定义了OptionSetType协议【在Swift 1.2中是使用RawOptionSetType,相比较而言已经改进了不少】,它的声明如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

/// Supplies convenient conformance to `SetAlgebraType` for any type

/// whose `RawValue` is a `BitwiseOperationsType`.  For example:

///

///     struct PackagingOptions : OptionSetType {

///       let rawValue: Int

///       init(rawValue: Int) { self.rawValue = rawValue }

///

///       static let Box = PackagingOptions(rawValue: 1)

///       static let Carton = PackagingOptions(rawValue: 2)

///       static let Bag = PackagingOptions(rawValue: 4)

///       static let Satchel = PackagingOptions(rawValue: 8)

///       static let BoxOrBag: PackagingOptions = [Box, Bag]

///       static let BoxOrCartonOrBag: PackagingOptions = [Box, Carton, Bag]

///     }

///

/// In the example above, `PackagingOptions.Element` is the same type

/// as `PackagingOptions`, and instance `a` subsumes instance `b` if

/// and only if `a.rawValue & b.rawValue == b.rawValue`.

protocol OptionSetType : SetAlgebraType, RawRepresentable {

/// An `OptionSet`‘s `Element` type is normally `Self`.

typealias Element = Self

/// Convert from a value of `RawValue`, succeeding unconditionally.

init(rawValue: Self.RawValue)

}

从字面上来理解,OptionSetType是选项集合类型,它定义了一些基本操作,包括集合操作(union, intersect, exclusiveOr)、成员管理(contains, insert, remove)、位操作(unionInPlace, intersectInPlace, exclusiveOrInPlace)以及其它的一些基本操作。

作为示例,我们来定义一个表示方向的选项集合,通常我们是定义一个实现OptionSetType协议的结构体,如下所示:


1

2

3

4

5

6

7

8

9

10

struct Directions: OptionSetType {

var rawValue:Int

init(rawValue: Int) {

self.rawValue = rawValue

}

static let Up: Directions = Directions(rawValue: 1 << 0)

static let Down: Directions = Directions(rawValue: 1 << 1)

static let Left: Directions = Directions(rawValue: 1 << 2)

static let Right: Directions = Directions(rawValue: 1 << 3)

}

所需要做的基本上就是这些。然后我们就可以创建Directions的实例了,如下所示:


1

2

3

4

let direction: Directions = Directions.Left

if direction == Directions.Left {

// ...

}

如果想同时支持两个方向,则可以如上处理:


1

2

3

4

let leftUp: Directions = [Directions.Left, Directions.Up]

if leftUp.contains(Directions.Left) && leftUp.contains(Directions.Up) {

// ...

}

如果leftUp同时包含Directions.Left和Directions.Up,则返回true。

这里还有另外一种方法来达到这个目的,就是我们在Directions结构体中直接声明声明Left和Up的静态常量,如下所示:


1

2

3

4

5

6

struct Directions: OptionSetType {

// ...

static let LeftUp: Directions = [Directions.Left, Directions.Up]

static let RightUp: Directions = [Directions.Right, Directions.Up]

// ...

}

这样,我们就可以以如下方式来执行上面的操作:


1

2

3

if leftUp == Directions.LeftUp {

// ...

}

当然,如果单一选项较多,而要去组合所有的情况,这种方法就显示笨拙了,这种情况下还是推荐使用contains方法。

总体来说,Swift中的对选项的支持没有Objective-C中的NS_OPTIONS来得简洁方便。而且在Swift 1.2的时候,我们还是可以使用”&“和”|”操作符的。下面这段代码在Swift 1.2上是OK的:


1

2

3

UIView.animateWithDuration(0.3, delay: 1.0, options: UIViewAnimationOptions.CurveEaseIn | UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in

// ...

}, completion: nil)

但到了Swift 2.0时,OptionSetType已经不再支持”&“和”|”操作了,因此,上面这段代码需要修改成:


1

2

3

UIView.animateWithDuration(0.3, delay: 1.0, options: [UIViewAnimationOptions.CurveEaseIn, UIViewAnimationOptions.CurveEaseInOut], animations: { () -> Void in

// ...

}, completion: nil)

不过,慢慢习惯就好。

参考

时间: 2024-09-30 00:27:48

在 Swift 中实现 NS_OPTIONS的相关文章

NS_OPTIONS在swift中怎么实现?

今天在一个ios培训网站上看到一篇博客,讲的是在NS_OPTIONS在Swift中的实现,写得还算比较深刻全面,小编对其进行整理后,分享出来,希望对大家在iOS应用开发上有所帮助吧. 在iOS开发中,我们常常需要定义一个枚举,以替代C语言枚举的定义方式,常用的方法就是在Objective-C中使用NS_ENUM和NS_OPTIONS.其中,NS_ENUM用于定义普通的枚举,NS_OPTIONS用于定义选项类型的枚举. 不同于Objective-C语言,Swift中的枚举增加了更多特性,它可以包含

Swift 中的Range和NSRange不同

Swift中的Ranges和Objective-C中的NSRange有很大的不同,我发现在处理Swift中Ranges相关的问题的时候,总是要花费比我想象的更多的时间.不过,现在回过头来看看,发现Swift中的Ranges的使用还是比较合理的,但是想要正确的使用Ranges真的需要一些特别的技巧. 看一个例子,下面这段代码展示的是截取以指定的字符开头和以指定的字符结尾的子字符串: ? 1 2 3 4 5 6 var str = "Hello, playground"   let ran

Swift 中的基础语法(二)

1.Swift 中的函数 /// 函数的定义 /// /// - Parameters: /// - x: 形参 /// - y: 形参 /// - Returns: 返回值 func sum(x: Int, y: Int) -> Int { return x + y } print(sum(x: 10, y: 20))   /* 外部参数就是在形参前面加了一个字 外部参数不会影响函数内部的细节 外部参数会让外部调用看起来更加直观 外部参数如果使用了'_',在外部调用函数时,会忽略形参的名字 &qu

Swift中的错误处理

前言 任何代码都会发生错误,这些错误有些是可以补救的,有些则只能让程序崩溃.良好的错误处理能够让你的代码健壮性提高,提高程序的稳定性. 本文的Swift版本:Swift 3 Objective C 返回nil 如果出错了,就返回空是Objective C中的一种常见的处理方式.因为在Objective C中,向nil发送消息是安全的.比如: - (instancetype)init { self = [super init]; if (self) { } //如果初始化失败,会返回nil ret

swift中代理的使用

下面以自定义的UITableViewCell的代理为例,记录一下swift中代理的使用 controller中的代码如 1 // 2 // ViewController.swift 3 // simpleDemo 4 // 5 // Created by liubo on 16/7/25. 6 // Copyright © 2016年 liubo. All rights reserved. 7 // 8 9 import UIKit 10 11 class ViewController: UIV

SWIFT中的repeat...while

SWIFT中的repeat...while类似于JAVA\.NET中的 do while.大同小异只是把do换成了repeat var index = 10 repeat{ print(index) index-- } while(index>0)

Swift中的结构体,类,协议,扩展和闭包的用法以及? 和 !的区别

// MARK: - 1.结构体 //1.声明一个结构体 struct Rect {    // 声明结构体变量的属性(存储属性)    var x:Float    var y:Float    var width:Float    var height:Float    // 声明结构体属性,要使用static    static var description:String?        // 声明一个计算属性(是用来专门计算结构体变量属性的setter,和getter方法,其本身没有存

Swift中编写单例的正确方式

Swift中编写单例的正确方式 2015-12-07 10:23 编辑: yunpeng.hu 分类:Swift 来源:CocoaChina翻译活动 14 10647 Objective-CSwift单例 招聘信息: Cocos2d-x 工程师 cocos2dx手游客户端主程 wp开发 iOS开发工程师 iOS软件工程师 iOS研发工程师 iOS讲师 iOS开发工程师 iOS高级开发工程师 iOS 高级软件工程师 iOS高级开发工程师 本文由CocoaChina译者leon(社区ID)翻译自kr

[Swift中错误]missing argument label &#39;greeting:&#39; in call

Swift 中出现这个问题:从第二个参数起,自动加上lable func sayHello(name:String? ,greeting:String)->String { let result = greeting + "," + (name ?? "Guest") + "!" return result } var nickname:String? nickname = "yc" //“Goodmorning前面应该