Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modifying struct instance variables within a Dispatch closure in Swift

I'm using the DEVELOPMENT-SNAPSHOT-2016-06-06-a version of Swift. I cannot seem to get around this issue, I've tried using @noescape in various places, but I still have the following error:

Closure cannot implicitly capture a mutating self parameter

To better explain, here is a simple example:

public struct ExampleStruct {
  let connectQueue = dispatch_queue_create("connectQueue", nil)
  var test = 10

  mutating func example() {
    if let connectQueue = self.connectQueue {
      dispatch_sync(connectQueue) {
        self.test = 20 // error happens here
      }
     }
   }
 }

Something must have changed in these Swift binaries that is now causing my previously working code to break. A workaround I want to avoid is making my struct a class, which does help in fixing the issue. Let me know if there is another way.

like image 725
tfrank377 Avatar asked Jun 27 '16 15:06

tfrank377


2 Answers

You are using Swift3 since you mentioned a recent dev snapshot of Swift. Try below and let me know if it works:

public struct ExampleStruct {
    let connectQueue = DispatchQueue(label: "connectQueue", attributes: .concurrent)//This creates a concurrent Queue

    var test = 10

    mutating func example() {
        connectQueue.sync { 
            self.test = 20
        }
    }
}

If you are interested in other types of queues, check these:

let serialQueue = DispatchQueue(label: "YOUR_QUEUE", attributes: .serial)
serialQueue.sync { 
    //
}

Get the mainQueue asynchronously and synchronously:

DispatchQueue.main.async {
   //async operations
}
DispatchQueue.main.sync {
    //sync operations
}

And if you are interested in Background:

DispatchQueue.global(attributes: .qosDefault).async {
  //async operations
}

You could refer this for new features in Swift3 and for changes to existing version: Migrating to Swift 2.3 or Swift 3 from Swift 2.2

like image 36
Santosh Avatar answered Oct 24 '22 19:10

Santosh


I cannot test it, because I'm not using a build with that error, but I'm pretty sure by capturing self explicitly you can fix it:

dispatch_sync(connectQueue) { [self] in
    self.test = 20
}

EDIT: Apparently it doesn't work, maybe you can try this (not very nice tbh):

var copy = self
dispatch_sync(connectQueue) {
    copy.test = 20
}
self = copy

If you want to read more on why, here is the responsible Swift proposal.

The new dispatch API makes the sync method @noreturn so you wouldn't need the explicit capture:

connectQueue.sync {
    test = 20
}
like image 187
Kametrixom Avatar answered Oct 24 '22 20:10

Kametrixom