如果我想给自己分配一个属性怎么办?
What if I want to assign a property to itself?
如果我尝试 运行 以下代码:
photographer = photographer
我收到错误:
Assigning a property to itself.
我想将 属性 分配给自身以强制 photographer
didSet
块到 运行.
这里有一个真实的例子:在Winter 2013 Stanford iOS course(13:20)的“16. Segues and Text Fields”讲座中,教授推荐写类似下面的代码:
@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
而不是 self.view.window
因为前者错误地强制视图在访问 view
属性 时加载; (4) reload()
方法(仅)为简单起见更新标签,因为它更类似于我的代码; (5) 添加了 photographer IBOutlet
标签来支持这个更简单的代码; (6) 由于我正在使用 Swift,isViewLoaded()
检查不再存在,仅仅是出于性能原因,现在 需要 来防止崩溃,因为IBOutlet 被定义为 UILabel!
而不是 UILabel?
因此在加载视图之前尝试访问它会使应用程序崩溃;这在 Objective-C 中不是强制性的,因为它使用空对象模式。
我们两次调用reload的原因是因为我们不知道属性是在创建视图之前还是之后设置。例如,用户可能首先设置 属性,然后呈现视图控制器,或者他们可能呈现视图控制器,然后更新 属性.
我喜欢这个 属性 与视图加载时间无关的方式(最好不要对视图加载时间做出任何假设),所以我想使用相同的模式(仅略微修改)在我自己的代码中:
@IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
photographerLabel?.text = photographer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
photographer = photographer
}
这里不是创建一个从两个地方调用的新方法,我只想要 didSet
块中的代码。我希望 viewDidLoad
强制调用 didSet
,所以我将 属性 分配给它自己。 Swift 不允许我这样做。如何强制调用 didSet
?
在 Swift 3.1 之前,您可以将 属性 name
分配给自身:
name = (name)
但这现在给出了同样的错误:"assigning a property to itself".
还有很多其他方法可以解决这个问题,包括引入临时变量:
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 = [=12=].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([=12=]),2) }
invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
}
}
let test = Test()
test.testit()
对所有这些选项都做得很好。然而,在 iOS 10.3 中,Apple 禁止了其中一些方式,并且很可能在未来会再次这样做。
Note: To avoid the risk and future errors, I will use a temporary variable.
我们可以为此创建一个简单的函数:
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
有一些很好的解决方法,但这样做没有什么意义。
如果程序员(代码的未来维护者)看到这样的代码:
a = a
他们会删除它。
这样的语句(或解决方法)永远不应出现在您的代码中。
如果您的 属性 看起来像这样:
var a: Int {
didSet {
// code
}
}
那么通过赋值 a = a
.
调用 didSet
处理程序不是一个好主意
如果未来的维护者像这样向 didSet
添加性能改进会怎样?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
真正的解决方案是重构:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
而不是 a = a
直接调用 updateA()
.
如果我们谈论的是出口,一个合适的解决方案是在第一次分配之前强制加载视图:
@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
中的代码变得不必要。
现在您可能会问 "why should I load the view if I don't need it yet? I want only to store my variables here for future use"。如果这就是您的要求,则意味着您正在使用视图控制器作为模型 class,只是为了存储数据。这本身就是一个架构问题。如果您不想使用控制器,甚至不要实例化它。使用模型 class 来存储您的数据。
创建一个 didSet 调用的函数,然后在您想要更新某些内容时调用该函数?似乎这可以防止开发人员走向 WTF?将来
我希望有一天 #Swift 开发人员会修复这个 miscuzzi :)
简易拐杖:
func itself<T>(_ value: T) -> T {
return value
}
使用:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(选项包括)
如果我尝试 运行 以下代码:
photographer = photographer
我收到错误:
Assigning a property to itself.
我想将 属性 分配给自身以强制 photographer
didSet
块到 运行.
这里有一个真实的例子:在Winter 2013 Stanford iOS course(13:20)的“16. Segues and Text Fields”讲座中,教授推荐写类似下面的代码:
@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
而不是 self.view.window
因为前者错误地强制视图在访问 view
属性 时加载; (4) reload()
方法(仅)为简单起见更新标签,因为它更类似于我的代码; (5) 添加了 photographer IBOutlet
标签来支持这个更简单的代码; (6) 由于我正在使用 Swift,isViewLoaded()
检查不再存在,仅仅是出于性能原因,现在 需要 来防止崩溃,因为IBOutlet 被定义为 UILabel!
而不是 UILabel?
因此在加载视图之前尝试访问它会使应用程序崩溃;这在 Objective-C 中不是强制性的,因为它使用空对象模式。
我们两次调用reload的原因是因为我们不知道属性是在创建视图之前还是之后设置。例如,用户可能首先设置 属性,然后呈现视图控制器,或者他们可能呈现视图控制器,然后更新 属性.
我喜欢这个 属性 与视图加载时间无关的方式(最好不要对视图加载时间做出任何假设),所以我想使用相同的模式(仅略微修改)在我自己的代码中:
@IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
photographerLabel?.text = photographer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
photographer = photographer
}
这里不是创建一个从两个地方调用的新方法,我只想要 didSet
块中的代码。我希望 viewDidLoad
强制调用 didSet
,所以我将 属性 分配给它自己。 Swift 不允许我这样做。如何强制调用 didSet
?
在 Swift 3.1 之前,您可以将 属性 name
分配给自身:
name = (name)
但这现在给出了同样的错误:"assigning a property to itself".
还有很多其他方法可以解决这个问题,包括引入临时变量:
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 = [=12=].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([=12=]),2) }
invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
}
}
let test = Test()
test.testit()
Note: To avoid the risk and future errors, I will use a temporary variable.
我们可以为此创建一个简单的函数:
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
有一些很好的解决方法,但这样做没有什么意义。 如果程序员(代码的未来维护者)看到这样的代码:
a = a
他们会删除它。
这样的语句(或解决方法)永远不应出现在您的代码中。
如果您的 属性 看起来像这样:
var a: Int {
didSet {
// code
}
}
那么通过赋值 a = a
.
didSet
处理程序不是一个好主意
如果未来的维护者像这样向 didSet
添加性能改进会怎样?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
真正的解决方案是重构:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
而不是 a = a
直接调用 updateA()
.
如果我们谈论的是出口,一个合适的解决方案是在第一次分配之前强制加载视图:
@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
中的代码变得不必要。
现在您可能会问 "why should I load the view if I don't need it yet? I want only to store my variables here for future use"。如果这就是您的要求,则意味着您正在使用视图控制器作为模型 class,只是为了存储数据。这本身就是一个架构问题。如果您不想使用控制器,甚至不要实例化它。使用模型 class 来存储您的数据。
创建一个 didSet 调用的函数,然后在您想要更新某些内容时调用该函数?似乎这可以防止开发人员走向 WTF?将来
我希望有一天 #Swift 开发人员会修复这个 miscuzzi :)
简易拐杖:
func itself<T>(_ value: T) -> T {
return value
}
使用:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(选项包括)