文档章节

在 Swift 中实现 NS_OPTIONS

hejunbinlan
 hejunbinlan
发布于 2015/09/17 10:47
字数 1387
阅读 28
收藏 0

805E1YYX3P.jpg

从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)

不过,慢慢习惯就好。

参考

本文转载自:http://www.cocoachina.com/swift/20150825/13079.html

hejunbinlan
粉丝 42
博文 596
码字总数 21569
作品 0
浦东
高级程序员
私信 提问
从YYModel源码中可以学到什么:前篇

前言 一个高性能模型框架。 作者在上给出的性能对比图(iphone 6 y:时间) :具体以下特点:高性能、自动类型转换、类型安全、非侵入性、轻量等。 关于如何使用查看文档和示例【传送门】。 本...

Owenli_千
2017/12/29
0
0
Objective-C 枚举 中位运算 的使用

前言 Enum,也就是枚举,从C语言开始就有了,C++、Java、Objective-C、Swift这些语言,当然都有对应的枚举类型,功能可能有多有少,但是最核心的还是一个—-规范的定义代码中的状态、选项等“...

法斗斗
2018/07/10
25
0
在同个工程中使用 Swift 和 Objective-C(Swift 2.0更新)

本节包含内容: Mix and Match 概述(Mix and Match Overview) 在同个应用的 target 中导入(Importing Code from Within the Same App Target) 在同个 Framework 的 target 中导入(Impor...

法斗斗
2016/03/01
7
0
NS_ASSUME_NONNULL_BEGIN && NS_ASSUME_NONNULL_END

1.NSASSUMENONNULLBEGIN && NSASSUMENONNULLEND 在中存在类型,也就是使用?和!声明的变量。但是OC里面没有这个特征,因为在XCODE6.3之后出现新的关键词定义用于OC转SWIFT时候可以区分到底是...

贝勒老爷
04/24
0
0
ARKit从入门到精通(4)-ARKit全框架API大全

转载请注明出处:ARKit从入门到精通(4)-ARKit全框架API大全 1.1-ARKit框架简介 1.2-ARAnchor 1.3-ARCamera 1.4-ARError 1.5-ARFrame 1.6-ARHitTestResult 1.7-ARLightEstimate 1.8-ARPlan......

u013263917
2017/06/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何编写高质量的 JS 函数(1) -- 敲山震虎篇

本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm7Xi7JxQ 作者:杨昆 一千个读者,有一千个哈姆雷特。 此系列文章将会从函数的执行机制、鲁棒性、函...

vivo互联网技术
53分钟前
5
0
学会这5个Excel技巧,让你拒绝加班

在网上,随处都可以看到Excel技巧,估计已看腻了吧?但下面5个Excel技巧会让你相见恨晚。关键的是它们个个还很实用 图一 技巧1:快速删除边框 有时当我们处理数据需要去掉边框,按Ctrl+Shif...

干货趣分享
今天
11
0
JS基础-该如何理解原型、原型链?

JS的原型、原型链一直是比较难理解的内容,不少初学者甚至有一定经验的老鸟都不一定能完全说清楚,更多的"很可能"是一知半解,而这部分内容又是JS的核心内容,想要技术进阶的话肯定不能对这个...

OBKoro1
今天
10
0
高防CDN的出现是为了解决网站的哪些问题?

高防CDN是为了更好的服务网络而出现的,是通过高防DNS来实现的。高防CDN是通过智能化的系统判断来路,再反馈给用户,可以减轻用户使用过程的复杂程度。通过智能DNS解析,能让网站访问者连接到...

云漫网络Ruan
今天
15
0
OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
3.3K
25

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部