4

In Swift Collections are pass by value by default and we can user inout to make it pass by reference in function arguments but how can we do it in closure capture variables?

var list = [1, 2, 3]
func edit(inout list: [Int]) {
    list.append(4)
    dispatch_async(dispatch_get_main_queue()) {
        list.append(5)
    }
}
edit(&list)
...// after dispatch_async was executed 
NSLog("\(list)")

Result will be [1, 2, 3, 4]

How can I modify the original variable () inside closure?

UPDATE:

Actually I have a workaround to handle this case by putting the array into an object so I can pass this object to the function by reference and we can modify the same array instance inside the function. but I want see any clever way to archive that

Alan
  • 123
  • 1
  • 8

1 Answers1

2

For getting a variable escape from a closure you need @escaping, check this out. A workaround is to put a completion function as an argument rather than an inout variable.

class MyClass {
    static func edit(_ list: [Int], _ completion: @escaping ([Int]) -> ()) {
        var list = list
        list.append(4)
        DispatchQueue.main.async() {
            list.append(5)
            completion(list)
        }
    }
}

var myList = [1, 2, 3]
MyClass.edit(myList) { (list) in
    myList = list
    print("My list after editing: \(myList)")
}
print("My list without editing: \(myList)")

Note: The above example is for Swift 3 where inout parameters are not allowed to be captured in closures. Based on your post, you may be using a lower version of Swift where you might be able to use inout rather than setting a mutable copy of the list: var list = list. However, the logic is very similar.

For more information, check the Limiting inout capture to @noescape contexts in Swift evolution:

Swift's behavior when closures capture inout parameters and escape their enclosing context is a common source of confusion. We should disallow implicit capture of inout parameters except in @noescape closures.

Lawliet
  • 3,438
  • 2
  • 17
  • 28
  • Thanks for your response but any chance that to implement it without access any instance variables inside the function? you can treat it like a static function – Alan Jul 28 '17 at 07:09
  • Thanks, Your answer is a way to get back the new copy of array and we can update the instance array variable in the completion closure. but I want to know any chance that "directly" modify the original array after passing into a function . sorry for my bad explanation in the question. I have tried your code and the result for the global `list` variable is still [1, 2, 3] – Alan Jul 28 '17 at 08:03
  • I updated my answer again to use `myList` rather than `list` to make it less confusing. To get the edited `myList` you need to wait until the edit gets completed. If you want to edit the `myList` directly, just make it as a class-level variable in your class and use something like `self.myList` in the closure. As you don't want to go for that approach at the first place, you cannot modify the `myList` directly. For the reason, check out the link I provided in my answer. – Lawliet Jul 28 '17 at 08:43