作者:KHANLOU,原文链接,原文日期:2017-03-28
译者:四娘;校对:Cwift;定稿:CMB

Swift 标准库里最容易被滥用的就是 Sequence 的 enumerated() 函数。这个函数会返回一个新的序列,包含了初始序列里的所有元素,以及与元素相对应的编号。

enumerated() 很容易被误解。因为它给每一个元素都提供了一个编号,对于很多问题来说这是一个很简便的方案。然而,这些问题大多数都可以被另一种方式更好的解决,让我们来看一下其中的一些例子吧,要注意理解它们有什么问题,然后如何使用更好的抽象去解决它们。

使用 enumerated() 最关键的问题在于大家都认为它返回的是每一个元素和元素的索引值,但实际上并不是这样的。因为它可以适用于所有序列,而序列是不能保证有索引的,由此可知它返回的并不是索引值。下面的代码里,这个变量的名字是 offset,而不是 index,这是接下来文章里会默认使用的命名方式。offset 总是一个整型,从 0 开始,间隔为 1,跟每一个元素逐一对应。对于 Array,这刚好跟它的索引值完全一致,但除此之外的其他所有类型,都不会有这种巧合发生。让我们来看一个例子:

let array = ["a","b","c","d","e"]
let arraySlice = array[2..<5]
arraySlice[2] // => "c"
arraySlice.enumerated().first // => (0,"c")
arraySlice[0] // fatalError

我们的变量 arraySlice,毫无疑问是 ArraySlice 类型。然而,它的startIndex 很明显是 2,而不是 0,但当我们调用 enumerated()first 的时候,它会返回一个元组,包含了一个offset,值为 0,以及它的第一个元素 “c”。

你以为,你会获得与下面等价的代码

zip(array.indices,array)

但实际上你获取到的是这个

zip((0..<array.count),array)

如果你不是在使用 Array 的话,随时可能会产生错误的行为。

而且实际上你获取到的是一个 offset,而不是 index,使用 enumerated() 也会有别的问题。很多时候你也许想用 enumerated(), 但有别的更好的抽象可以使用。让我们来看一些例子。

我见到 enumerated() 最常用的方式是对一个数组执行 enumerated,使用返回的 offset 来获取另一个数组对应的元素。

for (offset,model) in models.enumerated() {
    let viewController = viewControllers[offset]
    viewController.model = model
}

虽然这段代码可以正常运作,但前提是 modelsviewControllers 都是 Array 类型,使用整型来作为索引值类型,从 0 开始。另一个前提是这两个数组拥有相同的长度。如果models 的数组长度比 viewControllers 短的话,就会崩溃。我们还多了一个没有实际意义的多余的变量 offset。一个简洁的 Swift 实现方式应该是:

for (model,viewController) in zip(models,viewControllers) {
    viewController.model = model
}

这段代码更加简洁,而且适用于所有 Sequence 类型,而且可以安全地处理不等长的数组。

让我们看看另一个例子,这段代码给第一个 imageView 和它的容器以及每个 imageView 之间添加了一段 autolayout 的约束

for (offset,imageView) in imageViews.enumerated() {
    if offset == 0 {
        imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
    } else {
        let imagetoAnchor = imageView[offset - 1]
        imageView.leadingAnchor.constraint(equalTo: imagetoAnchor.trailingAnchor).isActive = true
    }
}

这段示例代码也有同样的问题,我们想要成对的元素,但使用 enumerated() 去获取索引以便后续操作的时候,我们就需要手动去处理索引,这并没有必要。zip 在这种情况下也适用。

首先,处理容器和第一个元素的约束:

imageViews.first?.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true

接着,我们来把元素拼成一对:

for (left,right) in zip(imageViews,imageViews.dropFirst()) {
    left.trailingAnchor.constraint(equalTo: right.leadingAnchor).isActive = true
}

搞定,没有索引值,任何 Sequence 类型都适用,而且更加简洁。

(你也可以把这个拼对的操作封装进 extension 里,我会考虑命名为 .eachPair()

这里介绍一下 enumerated() 的使用姿势。因为你获取到的并非是索引值,而是一个整型,所以当你需要一个数字去对应到每一个元素的时候,就很适合使用 enumerated()。例如,你需要在垂直方向等距摆放多个 view,每一个 view 都需要一个 y,等于某个高度乘以 offset,enumerated() 就很适合。下面是一个例子:

for (offset,view) in views.enumerated() {
    view.frame.origin.y = offset * view.frame.height
}

因为这里的 offset 是作为一个数字去使用,enumerated()就可以正常运作。

使用的规则很简单:如果你是想用 enumerated() 去获取索引,那也许会有更好的方式去解决你的问题,如果你是想把它作为一个数字去使用,那就很适合

你需要的大概不是 enumerated的更多相关文章

  1. html5录音功能实战示例

    这篇文章主要介绍了html5录音功能实战示例的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. ios – 如何在swift3中增加String.Index?

    在swift2.3中运算符用于string.index增加例如.一世我改为swift3代码发生了“一元运算符”不能应用于’@valueString.Index’类型的操作数(又名’@lvalueString.CharacterView.Index’)“在swift3中我改写了例如.i=1但是这段代码无法解决.请帮我.解决方法String.Index是String.CharacterView.Ind

  3. ios – CoreData有序关系 – 使用NSFetchRequest批量取消

    或者,是否存在批量不支持的API,它不是私有的?解决方法目前我有一个解决方案,但不是一个干净的解决方案:我希望按照有序关系中的20个小组进行批量修改.所以,每次我索引一个索引,它的索引除以20,我为接下来的20使用新的NSFetchRequest,并通过调用公共字段名称来解除它们.

  4. ios – Swift中的PageViewController当前页面索引

    我想获取一个pageViewController的当前索引,我不知道我如何获取可见页索引.解决方法您可以使用didFinishAnimating,并将标签设置为查看控制器.尝试这个

  5. ios – OpenGL – 为什么GL_ELEMENT_ARRAY_BUFFER的索引?

    我目前是OpenGLES2.0领域的新手,希望尽可能地了解绑定,缓冲区,着色器等.截至目前,我只是想了解GL_ELEMENT_ARRAY_BUFFER和GL_ARRAY_BUFFER之间的差异,以及何时使用每个注释的预设.我目前的理解使我相信GL_ELEMENT_ARRAY_BUFFER是专门用于所述三角形的索引,而另一个则是其他的.有人可以详细说明为什么,如果这是正确的?GL_ELEMENT_A

  6. 如何恢复索引功能? (Xcode中)

    我的一个项目刚刚开始干扰索引过程.索引过程在中途冻结,然后突然停止,导致SourceKitService崩溃.我根本无法找到错误的代码;因为似乎没有!)–但它无法被索引.最初,我以为它是一个Xcode7.2的问题,所以升级到最新的beta(7.3);但是问题依然存在.我无法恢复到我的旧代码,因为太多的工作将被撤销,我无法发现特定的文件.崩溃报告是here.为了澄清,Xcode本身不会崩溃,只有索引过程.关于如何解决这个问题的任何想法?

  7. ios – Swift:通过索引移动数组中的元素

    给定n个元素的阵列,即vararray=[1,2,3,4,5]我可以写一个扩展到Array,所以我可以修改数组来实现这个输出:[2,5,1]:有没有办法实现这样的功能,可以通过任何索引(正或负)来移动数组.我可以用if-else子句强制执行这个功能,但是我正在寻找的是功能实现.算法很简单:>按提供的索引将数组拆分成两个>将第一个数组追加到第二个数组的末尾有没有什么办法实现它的功能风格?

  8. ios – 从imageview点击手势获取索引或标签值

    这是来自应用商店的图像,只要我们搜索任何应用程序.我也想添加相同的scrollview概念,它显示当前图像与上一个和下一个图像的小预览.我可以在Samplecode的帮助下做出这个观点.但是当用户点击任何图像时,没有找到任何解决方案来获取索引或标签值.所以我可以打开每个图像的详细页面.如果有人有这个想法,请帮助我.提前致谢.解决方法将手势识别器添加到必要的图像视图中:然后在手势处理程序中访问附加到的视图手势识别器:

  9. ios – 不能下标'[NSObject:AnyObject]类型的值?具有“String”类型的索引

    意味着一个可选的类型,这意味着你试图在本质上是一个枚举上调用一个下标.当你尝试这样做时,没有下标声明,所以系统阻塞.通过添加?我们在说,如果可能,打开这个值,然后调用下标.这样一来,系统就会推测出下面的声明类型[NSObject:AnyObject],一切都可以.你也可以使用!强制解开,但如果苹果没有,这将会崩溃.写另一种可能的方式是:这样,苹果不再是可选的,它将始终具有下标语法.不需要解开包装

  10. iOS DeepLinking中是否需要Google App Indexing SDK?

    我想在我的网页和iOS应用中使用GoogleAppIndexing.我确实支持使用ApplesSearch的UniversalLinks(或Googlelingo中的深层链接)并相应地设置我的网页.从Googlesdocumentation开始,我无法确定是否真的需要添加GoogleAppIndexingSDK.SDK没有给我任何必需的功能,我宁愿跳过它–但谷歌是否依靠SDK才能做到这一点?我没有

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部