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.
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.
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:
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?
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)
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.
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.