4

I have an init that takes in an instance of a class (which I hope everyone knows that that means it's pass-by-reference)

I want to be able to copy the object and store in on two class instance variables, such that, I have a function that is meant to act as a "reset" where it will set any changes I had made up to a certain point go back to what it was before.

so something like:

convenience init(_ item:Item?){
    self.init()
    self.item = item
    self.undoItem = item
}


func reset(){
    self.item = self.undoItem
    self.reloadInfo()
}

I haven't had much success with what should be a relatively straight forward solution. I'm just too new to Swift and iOS development.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
daredevil1234
  • 1,303
  • 1
  • 10
  • 34
  • What class are these two methods in? Update your question with sample code that demonstrates the issue using these methods. Clarify what actually happens with the code and what you want to happen. – rmaddy Jul 26 '17 at 00:44
  • I don't understand why that is hard to understand. I want to figure out how to be able to make a copy of the item passed into the init so that I can store it into two different places so that they do not share the same reference. This will let me make changes to one, and if the user decided to undo those changes, the reset function is what I could call to do that. – daredevil1234 Jul 26 '17 at 00:49
  • 1
    Make sure `Item` has a copy initializer, and use it. Note that if you don't make a deep copy, you may still have issues if any of the properties are reference types, since if you make a shallow copy you'll have two different `Item` objects, but they'll both refer to the same instances of their reference type member variables. – Crowman Jul 26 '17 at 01:11
  • NSCopying https://developer.apple.com/documentation/foundation/nscopying – N3SS4H Jul 26 '17 at 01:40
  • Possible duplicate of [Implementing copy() in Swift](https://stackoverflow.com/questions/24242629/implementing-copy-in-swift) –  Jul 26 '17 at 02:04
  • Nitpick: `item` is *not* passed by reference to the initialiser; it's passed *by value* (all Swift parameters, with the exception of parameters that take `&` arguments are pass-by-value). It just so happens that the value being passed here is an optional reference. – Hamish Jul 26 '17 at 10:00
  • @Hamish, class instances are always pass by reference, as are functions. Everything else is pass by value, unless you use the inout keyword... although, inout is not technically pass by reference to my understanding, it behaves as such. – daredevil1234 Jul 26 '17 at 15:00
  • @daredevil1234 See [this comment chain](https://stackoverflow.com/questions/27364117/is-swift-pass-by-value-or-pass-by-reference#comment43191855_27366050). And `inout` is compiled to a pass-by-reference, but has the semantics of copy-in copy-out. – Hamish Jul 26 '17 at 15:04
  • @Hamish that comment chain you sent supports what I am saying though. Since item is a class instance, it is a reference, hence, when I am changing item, it is also changing undoItem, preventing me from undoing. I can clearly state that my code is acting exactly like the apple developer link pass by reference example in the "What is the difference" section. Additionally here: https://thatthinginswift.com/value-and-reference-types/ and look at the first note in the ARC section of Swift documentation – daredevil1234 Jul 26 '17 at 15:54
  • @daredevil1234 Yes, you're working with reference types. But pass-by-value/pass-by-reference is *completely unrelated* to whether you're dealing with value types or reference types (as said in the comment chain I linked to). You can pass a value type both by value or by reference. You can also pass a reference type both by value or by reference. Consider that a reference *itself* is a value type; it gets copied when you pass it about (but of course these copies all point to the same instance). – Hamish Jul 26 '17 at 16:01
  • @daredevil1234 If you were to pass a reference type *by reference*, the callee would have a reference to a reference. Passing by value means the callee just deals with a copy of the reference you passed (but again, this copy still points to the same instance). – Hamish Jul 26 '17 at 16:01
  • @Hamish I understand what you're saying, that they are passing a reference type, hence technically making it pass-by-value with a type of a reference that is pointing to a spot in memory... but apple ARC docs refer to passing a reference type as a parameter as pass-by-reference and contrast it to pass-by-value used for Structs an Enums. I'm only adhering to their wording, even though it is in fact just making a copy. Strictly, it is not pass-by-reference. – daredevil1234 Jul 26 '17 at 16:31
  • @Hamish However, "the called functions' parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself)" is the approximate definition of pass-by-reference. If making copies of the parameter, which is a reference type, accomplishes this behavior, could it not be regarded as passing by reference? I'd like to point out, that C++ does the same thing as swift (to my knowledge) in that you must pass a pointer type. Passing this pointer type and assigning it to other values copies the pointer and yet, this is regarding as passing-by-reference – daredevil1234 Jul 26 '17 at 16:34
  • whereas, swift, doing the same thing, people get really technical about and claim it isn't pass-by-reference – daredevil1234 Jul 26 '17 at 16:34
  • @daredevil1234 The important part of the quote you make is "*the variable itself*". Say you have `class C {}; var c = C(); foo(c)`. `c` (the reference, that is) is passed by value into `foo`. The caller has a variable with the value of `c`, and the callee has a *separate* variable with the value of `c`. They do *not* share the same variable; if the callee was to mutate the parameter, that change wouldn't be visible to the caller (in fact in Swift 3, you can't directly mutate parameters; you have to make a mutable copy anyway). – Hamish Jul 28 '17 at 14:07
  • A pass-by-reference is where both the callee and caller *share* the same variable for the parameter; if the callee mutates the parameter, that change is visible to the caller (such as with `inout`). – Hamish Jul 28 '17 at 14:07
  • @daredevil1234 This is all tangential to the actual question you posed btw; which is why I originally marked my comment as a "nitpick" :) – Hamish Jul 28 '17 at 18:37

2 Answers2

0

Use of swift structures could be an option for you.

struct Item {
    //
}

Structures in Swift are value types, and they are copied by value rather than reference.

Example

struct Item {
    var value: Int = 1
}

var item1 = Item()
var item2 = item1
item2.value = 20

print("\(item1.value)") // 1
print("\(item2.value)") // 20

In the above example item1.value is 1 and item2.value is 20. The following line creates a copy of item1 and assigns it to item2:

var item2 = item1

From this line any change to item2 is not reflected in item1.

Conclusion

Your problem can be solved by defining Item as a struct

Bilal
  • 18,478
  • 8
  • 57
  • 72
0

Wrote the following (with the help of a friend) in playground:

protocol Copyable {
   func copyOfValues() -> AnyObject
}

extension Copyable where Self: NSObject {
    func copyOfValues() -> AnyObject{
        var copyOfOriginal = Self()
        let mirror = Mirror(reflecting: self)
        for (label, value) in mirror.children {
            if let label = label {
                copyOfOriginal.setValue(value, forKey: label)
            }
       }

        return copyOfOriginal
    }
}

class Test: NSObject, Copyable {
    var a = 1
    var b = 2
}

var test = Test()
var copy = test.copyOfValues() as! Test

print(dump(test))
print(dump(copy))
copy.a = 10
print(dump(test))
print(dump(copy))

This is a nice and simple function so that I can obtain copy of my class instances in swift. In swift, since they are a reference type (and I am not sure if you can dereference it or whatnot) you would basically have to write a custom copy function for your objects every time. Well, Now I wrote this, so as long as you are using a subclass of NSObject and use this protocol, you'll be fine.

This has worked exactly as I need in my code

daredevil1234
  • 1,303
  • 1
  • 10
  • 34
  • I know this example is 3 years old, but it crashes in swift 5.1: `[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key a`. You'd need to put `@objc dynamic ` in front of the properties to fix it. – pulse4life May 04 '20 at 14:28