What if I want to assign a property to itself?

后端 未结 5 1801
孤街浪徒
孤街浪徒 2020-12-30 00:37

If I attempt to run the following code:

photographer = photographer

I get the error:

Assigning a property to itself

相关标签:
5条回答
  • 2020-12-30 01:16

    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

    0 讨论(0)
  • 2020-12-30 01:19

    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()
    
    0 讨论(0)
  • 2020-12-30 01:30

    @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.


    We can create a simple function for that:

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

    Would be used like: callSet(&foo)


    Or even a unary operator, if there is a fitting one ...

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

    Would be used like: +=foo

    0 讨论(0)
  • 2020-12-30 01:39

    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.

    0 讨论(0)
  • 2020-12-30 01:41

    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)


    0 讨论(0)
提交回复
热议问题