【问题标题】:Assigning let variable in fallible initializer swift 1.2在可犯错的初始化程序 swift 1.2 中分配 let 变量
【发布时间】:2015-06-16 16:46:58
【问题描述】:

我有一个带有易错初始化器的结构,不是实例方法,而是初始化器。更新到 1.2 后,当我尝试在初始化程序中分配 let 属性时,我收到以下错误 Cannot assign to 'aspectRatio' in self。我的代码如下:

import Foundation

public struct MediaItem
{
public let url: NSURL!
public let aspectRatio: Double

public var description: String { return (url.absoluteString ?? "no url") + " (aspect ratio = \(aspectRatio))" }

// MARK: - Private Implementation

init?(data: NSDictionary?) {
    var valid = false
    if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
        if let url = NSURL(string: urlString as String) {
            self.url = url
            let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
            let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
            if h != nil && w != nil && h?.doubleValue != 0 {
                aspectRatio = w!.doubleValue / h!.doubleValue
                valid = true
            }
        }
    }
    if !valid {
        return nil
    }
}

struct TwitterKey {
    static let MediaURL = "media_url_https"
    static let Width = "sizes.small.w"
    static let Height = "sizes.small.h"
}
}

我的问题是我该怎么做才能解决这个问题?

【问题讨论】:

  • 这不只是因为您有一个不可变变量(使用let)并且您分配给它两次(一次在初始化程序中 - 0,另一次在构造函数中) - 确实如果您只在构造函数中分配它并在 else 中分配 0,它就可以工作?
  • @Benjamin,在添加 else 语句并删除初始化程序后,我现在得到了 self used before all stored properties are initialized
  • @Riley,那个问题和我的有什么区别?我正在使用一个错误的初始化程序,而这个问题不是。
  • 赞成提供所有必要的代码,我只需将其复制并粘贴到应用程序中并尝试编译并复制您的问题。我希望每个提出问题的人都这样做。

标签: ios swift initializer


【解决方案1】:

Swift 1.2 已经关闭了一个与let 属性有关的漏洞:

新规则是 let 常量必须在使用前进行初始化(像 var 一样),并且它只能在初始化后被初始化,不能重新分配或改变。

该规则正是您试图违反的。 aspectRatio 是一个 let 属性你已经在它的声明中给了它一个值

public let aspectRatio: Double = 0

所以在我们到达初始化器之前,aspectRatio 有它的初始值——0。这是它唯一可以拥有的值。新规则意味着您可以永远再分配给aspectRatio甚至不能在初始化程序中

解决方案是(这总是正确的方法):在其声明中为其分配 no 值:

public let aspectRatio: Double

现在,在初始化器中,要么将其赋值为 0 将其赋值为 w!.doubleValue / h!.doubleValue。换句话说,注意初始化器中的每一个可能性,一次。这将是唯一时间,您可以通过一种或另一种方式为aspectRatio 赋值。

如果您仔细考虑一下,您会发现这是一种更加明智和一致的方法;以前,您对let 的含义有些推诿,而新规则正确地阻止了您这样做。


在您重写代码的过程中,您未能在您打算退出并返回nil 的情况下初始化所有属性。我知道这可能看起来违反直觉,但你不能那样做。您必须初始化所有属性即使您打算退出。我很清楚地讨论了这个in my book

一个可失败的类初始化器在完成它自己的所有初始化任务之前不能说return nil。因此,例如,指定初始化程序的可失败子类必须确保所有子类的属性都已初始化,并且必须先调用super.init(...),然后才能说出return nil。 (这里有一个有趣的讽刺:在它可以拆除实例之前,初始化器必须完成实例的构建。)

编辑:请注意,从 Swift 2.2 开始,此要求将被取消。 return nil初始化属性之前是合法的。这将使类初始化器与结构初始化器相提并论,这已经是合法的了。

【讨论】:

  • 这对我来说很有意义,但是我仍然收到此错误。 self used before all stored properties are initialized。我认为这是一个完全不同的问题是否正确?感谢您迄今为止的帮助。
  • @jason328 问题是我不知道你的 new 代码是什么样子的。我已经解释了您在问题中显示的代码的问题。我不知道你对它做了什么改变,这给你带来了另一个问题。
  • 我道歉。问题现已更新以反映最新的代码。
  • 使用新代码,您没有像在初始化程序中那样处理所有可能性。你在if let urlString = ...if let url = ... 的条件下做一些事情。但是,如果这些事情是真的呢?那么这些条件不是,代码永远不会执行——并且urlaspectRatio 永远不会被初始化。但是您必须初始化它们!这就是错误消息告诉您的内容。您必须涵盖所有种可能性。
  • 哦,我遗漏了一些东西。你必须这样做即使你将返回nil来标记失败。这听起来很古怪,但这是确保完美一致性的唯一方法。
猜你喜欢
  • 2015-06-16
  • 2017-04-22
  • 2021-10-19
  • 2017-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多