Grand Central Dispatch Tutorial for Swift 4: Part 1/2 | Ray Wenderlich

Learn all about multithreading, dispatch queues, and concurrency in the first part of this Swift 4 tutorial on Grand Central Dispatch.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2
1 Like

The started project has several errors

1 Like

This tutorial is based on Swift 4.2 and Xcode 10 beta in anticipation of the release this fall.

For the 9.4.1 users:
As a rule of thumb, for most of the “has no member” errors simply removing the period typically leads to the actual function name.

2 Likes

This will help too:
anywhere in a closure using [weak self] that Evan writes
guard let self = self else { ...
use
guard let strongSelf = self else { ...
and reference strongSelf instead of self.

1 Like

@edekhayser Any chance of keeping the old tutorial link around until the beta gets released?

@dvd.nzl The GM should be released in two weeks so you will have to wait until then. Thank you!

Hey can someone explain why in part one where we perform the read on the concurrentPhotoQueue we have the store property with in the getter and it’s forced unwrap, if I remove the bang it complains about the property being captured by the closure before being initialized, what exactly does this mean, and is force unwrapping the only option?

If you use var photosCopy: [Photo], you are expected to initialize it before you capture it in the closure. When photosCopy is [Photo]!, it is implicitly set equal to nil when it is captured by the closure, which allows it to be used here.

Looking more critically at this code, here is a better alternative:

var photos: [Photo] {
    return concurrentPhotoQueue.sync {
        return self.unsafePhotos
    }
}
1 Like

hi Evan,

I have a question, I will be grateful if you could help me with that.
I think we can make the PhotoManager thread safe using a serial queue as well. then when we want to read/write unsafePhotos just use that serial queue Async like this:
private let serialPhotoQueue =DispatchQueue(label: "serialPhotoQueue ")

func addPhoto(_ photo: Photo) {

serialPhotoQueue.async(flags: .barrier) { [weak self] in

guard let self = self else {
  return
}
self.unsafePhotos.append(photo)
DispatchQueue.main.async { [weak self] in
  self?.postContentAddedNotification()
}

}
}

var photos: [Photo] {
var photosCopy: [Photo]!
serialPhotoQueue.async {

photosCopy = self.unsafePhotos

}
return photosCopy
}
that also grantee the thread safety , am I right? if I am correct then may I know what is the advantage of your way in compare using a serial queue Async?

Thanks

@edekhayser Can you please help with this when you get a chance? Thank you - much appreciated! :]

Hi Artmis, sorry it took me so long to get back to you.

One thing that sticks out to me in your approach is the following:

var photos: [Photo] {
    var photosCopy: [Photo]!
    serialPhotoQueue.async {
        photosCopy = self.unsafePhotos
    }
    return photosCopy
}

Because the serial queue is given an async block, there is no guarantee that photosCopy = self.unsafePhotos will be called before the return statement, leading to the nil value being returned. The solution outlined in the tutorial avoids this by using a sync block. (I may be mistaken in my logic, and if so please let me know)

" The private initializer makes sure that the only PhotoManager is then one assigned to shared"

Is any anyone knows that why private initializer can make sure this?

@edekhayser Can you please help with this when you get a chance? Thank you - much appreciated! :]

If you have the following declaration:

class MyClass {
}

then you the following code is legal:

let a = MyClass()

By default, objects have an initializer that allows this to work. However, if you reimplement MyClass as follows:

class MyClass {
    private init() {}
}

then that code will not work. Instead, you’ll get this error: 'MyClass' initializer is inaccessible due to 'private' protection level. We can still call the initializer within the class, which is why static let shared = MyClass() works. Outside of the class implementation, though, you cannot create a new instance of MyClass.

Thanks for the excellent tutorial.
I wonder if we can use sync on write as well? Or async would make write more efficient?

Like this:

func addPhoto(_ photo: Photo) {
concurrentPhotoQueue.sync(flags: .barrier) { [weak self] in
// 1
guard let self = self else {
return
}

// 2
self.unsafePhotos.append(photo)

// 3
DispatchQueue.main.async { [weak self] in
  self?.postContentAddedNotification()
}

}
}

@edekhayser Can you please help with this when you get a chance? Thank you - much appreciated! :]

The problem with calling this setter synchronously is that you will be blocking the thread that calls addPhoto, which is unexpected. By calling it asynchronously, you will be freeing up that thread while setting the array asynchronously on the photos thread, which makes sure that any photo handling code is executed in the right order.

Thanks for exellent tutorial.
you say that " global concurrent queues that are shared by the whole system. " That means all the apps on my iphone use this queues at the same time? What if one of the buggy app locks a global queue? What happens to other apps? Thanks.

iOS apps are not allowed to run in the background indefinitely. For most apps, they can only run for a few seconds after they are “minimized”. So, you don’t need to really worry about these other apps blocking yours queues since the system will take care of it for you.