我有一个带有易错初始化程序的结构,不是实例方法,而是初始化程序。更新到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"
}
}
我的问题是如何解决此问题?
答案 0 :(得分:9)
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
是合法的。这将使类初始化器与struct初始化器相提并论,这已经是合法的。