If I attempt to run the following code:
photographer = photographer
I get the error:
Assigning a property to itself
Make a function that the didSet calls then call that function when you want to update something? Seems like this would guard against developers going WTF? in future
Prior to Swift 3.1 you could assign the property name
to itself with:
name = (name)
but this now gives the same error: "assigning a property to itself".
There are many other ways to work around this including introducing a temporary variable:
let temp = name
name = temp
This is just too fun not to be shared. I'm sure the community can come up with many more ways to do this, the crazier the better
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()
@vacawama did a great job with all those options. However in iOS 10.3, Apple banned some of these ways and most likely will be doing it in the future again.
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
}
Would be used like: callSet(&foo)
prefix operator +=
prefix func +=<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: +=foo
There are some good workarounds but there is little point in doing that. If a programmer (future maintainer of the code) sees code like this:
a = a
They will remove it.
Such a statement (or a workaround) should never appear in your code.
If your property looks like this:
var a: Int {
didSet {
// code
}
}
then it's a not a good idea to invoke the didSet
handler by assignment a = a
.
What if a future maintainer adds a performance improvement to the didSet
like this?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
The real solution is to refactor:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
And instead of a = a
directly call updateA()
.
If we are speaking about outlets, a suitable solution is to force the loading of views before assigning for the first time:
@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
}
}
That will make the code in viewDidLoad
unnecessary.
Now you might be asking "why should I load the view if I don't need it yet? I want only to store my variables here for future use". If that's what you are asking, it means you are using a view controller as your model class, just to store data. That's an architecture problem by itself. If you don't want to use a controller, don't even instantiate it. Use a model class to store your data.
I hope one day #Swift developers will fix this miscuzzi :)
Simple crutch:
func itself<T>(_ value: T) -> T {
return value
}
Use:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(optionals including)