如果我想为自己分配一个属性呢?

如果我试图运行下面的代码:

photographer = photographer 

我得到的错误:

为自己分配一个属性。


我想把自己的属性分配给强制photographer didSet块来运行。

下面是一个真实的例子:在2013年冬季斯坦福大学iOS课程 “13:16”的“16. Segges and Text Fields”课程中,教授build议编写类似如下的代码:

 @IBOutlet weak var photographerLabel: UILabel! var photographer: Photographer? { didSet { self.title = photographer.name if isViewLoaded() { reload() } } } override func viewDidLoad() { super.viewDidLoad() reload() } func reload() { photographerLabel.text = photographer.name } 

注意 :我做了如下修改:(1)代码从Objective-C切换到Swift; (2)因为它是在Swift中,我使用属性的didSet块而不是setPhotographer:方法; (3)而不是isViewLoaded我使用isViewLoaded因为前者错误地强制视图加载view属性的访问; (4) reload()方法(仅)为了简单起见而更新标签,并且因为它更类似于我的代码; (5)增加摄影师IBOutlet标签来支持这个更简单的代码; (6)因为我使用Swift, isViewLoaded()检查不再仅仅是出于性能原因而存在,现在需要防止崩溃,因为IBOutlet被定义为UILabel! 而不是UILabel? 因此在加载视图之前尝试访问它会使应用程序崩溃; 在Objective-C中这不是必须的,因为它使用空对象模式。

我们调用两次重新加载的原因是因为我们不知道在创build视图之前或之后是否设置属性。 例如,用户可能首先设置属性,然后呈现视图控制器,或者他们可能呈现视图控制器,然后更新属性。

我喜欢这个属性如何加载视图(最好不要对视图加载时间做任何假设),所以我想在我自己的代码中使用相同的模式(只是稍微修改):

 @IBOutlet weak var photographerLabel: UILabel? var photographer: Photographer? { didSet { photographerLabel?.text = photographer.name } } override func viewDidLoad() { super.viewDidLoad() photographer = photographer } 

在这里,而不是创build一个新的方法来调用两个地方,我只想在didSet块的代码。 我想viewDidLoad强制didSet被调用,所以我分配给自己的属性。 但斯威夫特不允许我这样做。 我如何强制didSet被调用?

Swift 3.1之前,您可以使用以下命令将属性name分配给自己:

 name = (name) 

但是现在给出了同样的错误: “为自己分配一个属性”

还有很多其他方法可以解决这个问题,包括引入一个临时variables:

 let temp = name name = temp 

这太好玩了,不被分享。 我相信社区可以想出更多办法来做到这一点,越疯越好

 class Test: NSObject { var name: String? { didSet { print("It was set") } } func testit() { // name = (name) // No longer works with Swift 3.1 (bug SR-4464) // (name) = name // No longer works with Swift 3.1 // (name) = (name) // No longer works with Swift 3.1 (name = name) name = [name][0] name = [name].last! name = [name].first! name = [1:name][1]! name = name ?? nil name = nil ?? name name = name ?? name name = {name}() name = Optional(name)! name = ImplicitlyUnwrappedOptional(name) name = true ? name : name name = false ? name : name let temp = name; name = temp name = name as Any as? String name = (name,0).0 name = (0,name).1 setValue(name, forKey: "name") // requires class derive from NSObject name = Unmanaged.passUnretained(self).takeUnretainedValue().name name = unsafeBitCast(name, to: type(of: name)) name = unsafeDowncast(self, to: type(of: self)).name perform(#selector(setter:name), with: name) // requires class derive from NSObject name = (self as Test).name unsafeBitCast(dlsym(dlopen("/usr/lib/libobjc.A.dylib",RTLD_NOW),"objc_msgSend"),to:(@convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject unsafeBitCast(class_getMethodImplementation(type(of: self), #selector(setter:name)), to:(@convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject unsafeBitCast(method(for: #selector(setter:name)),to:(@convention(c)(Any?,Selector,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject _ = UnsafeMutablePointer(&name) _ = UnsafeMutableRawPointer(&name) _ = UnsafeMutableBufferPointer(start: &name, count: 1) withUnsafePointer(to: &name) { name = $0.pointee } //Using NSInvocation, requires class derive from NSObject let invocation : NSObject = unsafeBitCast(method_getImplementation(class_getClassMethod(NSClassFromString("NSInvocation"), NSSelectorFromString("invocationWithMethodSignature:"))),to:(@convention(c)(AnyClass?,Selector,Any?)->Any).self)(NSClassFromString("NSInvocation"),NSSelectorFromString("invocationWithMethodSignature:"),unsafeBitCast(method(for: NSSelectorFromString("methodSignatureForSelector:"))!,to:(@convention(c)(Any?,Selector,Selector)->Any).self)(self,NSSelectorFromString("methodSignatureForSelector:"),#selector(setter:name))) as! NSObject unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setSelector:")),to:(@convention(c)(Any,Selector,Selector)->Void).self)(invocation,NSSelectorFromString("setSelector:"),#selector(setter:name)) var localVarName = name withUnsafePointer(to: &localVarName) { unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setArgument:atIndex:")),to:(@convention(c)(Any,Selector,OpaquePointer,NSInteger)->Void).self)(invocation,NSSelectorFromString("setArgument:atIndex:"), OpaquePointer($0),2) } invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self) } } let test = Test() test.testit() 

有一些很好的解决方法,但没有什么意义。 如果程序员(代码的未来维护者)看到这样的代码:

 a = a 

他们将删除它。

这样的陈述(或解决方法)不应该出现在你的代码中。

如果你的财产如下所示:

 var a: Int { didSet { // code } } 

那么通过赋值a = a来调用didSet处理程序并不是一个好主意。

如果一个未来的didSet像这样增加了对didSet的性能改进呢?

 var a: Int { didSet { guard a != oldValue else { return } // code } } 

真正的解决scheme是重构:

 var a: Int { didSet { self.updateA() } } fileprivate func updateA() { // the original code } 

而不是a = a直接调用updateA()

如果我们谈到网点,一个合适的解决scheme是在第一次分配之前强制加载视图:

 @IBOutlet weak var photographerLabel: UILabel? var photographer: Photographer? { didSet { _ = self.view // or self.loadViewIfNeeded() on iOS >= 9 photographerLabel?.text = photographer.name // we can use ! here, it makes no difference } } 

这将使viewDidLoad的代码不必要。

现在你可能会问“如果我不需要它,我为什么要加载视图呢?我只想把我的variables存储在这里以供将来使用” 。 如果这就是你所要求的,这意味着你正在使用一个视图控制器作为你的模型类,只是为了存储数据。 这本身就是一个架构问题。 如果你不想使用控制器,甚至不要实例化它。 使用模型类来存储数据。

@vacawama在所有这些选项上都做得很出色。 然而,在iOS 10.3中 ,苹果禁止了这些方法中的一部分,并且很可能在未来再次进行。

注意:为了避免风险和将来的错误,我将使用一个临时variables。


我们可以为此创build一个简单的函数:

 func callSet<T>(_ object: inout T) { let temporaryObject = object object = temporaryObject } 

将使用像: callSet(&foo)


甚至一个一元操作员,如果有一个合适的…

 prefix operator += prefix func +=<T>(_ object: inout T) { let temporaryObject = object object = temporaryObject } 

将被用作: +=foo

做一个函数,didSet调用然后调用该函数,当你想更新的东西? 似乎这样会防止开发商去跆拳道? 在未来