3

I have this class:

class MainView:UIView{

    var categories:[Category]!

}

i want to set the categories arg, but i need to pass it by reference not value. because it's more efficient and better.

so if i did this:

let mainView  = MainView()
mainView.categories = categoriesData.

then it pass it by value.

if i need to pass it by reference i could do that by using function inside the MainView()

class MainView:UIView{

    var categories:[Category]!
    fun setCategories(inout categories: Int){
            self.categories = categories;

    }

}

but if i don't want to use set function, How could i pass it by reference. e.g

mainView.categories = &categoriesData. but that doesn't work ?thanks

david
  • 3,310
  • 7
  • 36
  • 59
  • @matt thank you for your response. i don't got your point. i need to pass categories by reference because i don't want to make a copy, as this is redundant. am i wrong ? – david Feb 14 '16 at 11:58
  • @matt this line "mainView.categories = categoriesData" it doesn't copy the array to another array ? – david Feb 14 '16 at 12:00
  • @matt how it doesn't ? please have a look at this:http://stackoverflow.com/questions/28889705/in-swift-difference-between-array-vs-nsarray-vs-anyobject , it shows that array are passing by value – david Feb 14 '16 at 12:04
  • @matt why you have deleted your comments ? – david Feb 14 '16 at 13:55
  • What makes you think pass by reference is faster and better? You need to read up a bit about Swift. The compiler is _clever_. The array will only be copied if either the array or the original are modified, and in that case you _really_ don't want a reference, but a copy. – gnasher729 Feb 14 '16 at 15:18
  • @gnasher729 thanks , now i understand, exactly as vacawama answer. – david Feb 14 '16 at 15:27
  • And as I told you three hours ago – matt Feb 14 '16 at 15:29
  • yes matt thank you :) but I just needed more explanation – david Feb 14 '16 at 16:20

1 Answers1

6

Swift uses ARC (Automatic Reference Counting) when dealing with arrays, and it delays copying arrays until one of the copies is modified:

For example:

var a = [1, 2, 3, 4, 5]
let b = a
let c = a   // 1

a.append(6) // 2

print(a.count)
print(b.count)
print(c.count)

At step 1 above, there is only one copy of [1, 2, 3, 4, 5] in memory, and a, b, and c are references to it.

When a is modified in step 2, Swift gives a and new copy of the array and b and c continue to reference the original array. So now there are 2 copies of the array in memory.


Let's look at a much more involved example:

class Person: CustomStringConvertible {
    let name: String
    var friends: [Person] = []

    init(name: String) {
        self.name = name
    }

    var description: String { return name }
}

func createFredsFriends() -> [Person] {
    let barney = Person(name: "Barney")
    let wilma = Person(name: "Wilma")
    let betty = Person(name: "Betty")
    let friends = [barney, wilma, betty]  // 1

    return friends
}

func createFred() -> Person {
    let fred = Person(name: "Fred")

    let friends = createFredsFriends() // 2
    fred.friends = friends             // 3

    return fred
}

let fred = createFred()  // 4

print(fred.friends)  // [Barney, Wilma, Betty]
  • At step 1, the array of friends is created. It is referenced by the local variable friends.

  • This reference goes away when createFredsFriends() returns, and the only reference to the array is held then by the local variable friends at step 2. Ownership of the array has been passed.

  • At step 3, a second reference to the array has been assigned to the friends property of fred.

  • At step 4, createFred() has returned, so the local variable friends is gone and no longer references the array. The only reference is in the property of the fred object which is held by the variable fred.

So the array was created once, several references to it were created, and in the end there is a single reference to the array and all of this was done without a single copy operation.


Since Swift arrays are value types and copied when changed, you can't pass an array and then expect the original to be updated when the copy is. If you need that level of functionality, you can create a class wrapper for the array and then always access that array through the instance of that class.

Here I've modified the previous example to show how that would work:

// Class to wrap array so that everyone references the same copy
class FriendsWrapper {
    var friends: [Person]

    init(friends: [Person]) {
        self.friends = friends
    }
}

class Person: CustomStringConvertible {
    let name: String
    var friendsWrapper: FriendsWrapper?

    init(name: String) {
        self.name = name
    }

    func addFriend(friend: Person) {
        if let wrapper = friendsWrapper {
            wrapper.friends.append(friend)
        } else {
            friendsWrapper = FriendsWrapper(friends: [friend])
        }
    }

    var description: String { return name }
}

func createFredsFriends() -> [Person] {
    let barney = Person(name: "Barney")
    let wilma = Person(name: "Wilma")
    let betty = Person(name: "Betty")
    let friends = [barney, wilma, betty]

    return friends
}

func createFred() -> Person {
    let fred = Person(name: "Fred")

    let friendsWrapper = FriendsWrapper(friends: createFredsFriends())
    fred.friendsWrapper = friendsWrapper

    // Add friend to Fred object
    fred.addFriend(Person(name: "Bam Bam"))

    // Copy of array in local friendsWrapper is updated
    print(friendsWrapper.friends)  // [Barney, Wilma, Betty, Bam Bam]

    return fred
}

let fred = createFred()
vacawama
  • 150,663
  • 30
  • 266
  • 294
  • excuse me , if i need to pass an array by reference , so if i change the first array, the changes also happened to the second array. is it possible to do it this way:mainView.categories = &categoriesData – david Feb 14 '16 at 13:52
  • 1
    It's not possible to do that with a Swift array. You can create a class based wrapper object and have the array be the property of a class object and then pass the class object in. Since classes are always held as references, you will refer to the same array when accessed through the class. Something like `class friendsWrapper { var friends: [Person] }`. Then you'd access like `friendsWrapper.friends`. – vacawama Feb 14 '16 at 14:54
  • appreciate your help – david Feb 14 '16 at 14:58
  • I updated the answer to show an example of using a wrapper. – vacawama Feb 14 '16 at 15:24