10

Maybe this question requires a bit of context.

I've been working on my persistence layer using Core Data and found out that Core Data isn't thread-safe and thus requires NSManagedObjectContext to be confined to each one thread only.

So my approach is to create custom background thread NSManagedObjectContext which executes fetching, saving etc, while also to create main thread NSManagedObjectContext which will be used to get NSManagedObject from fetched NSManagedObjectId and pass it to caller method.

By default, Xcode generates template code related to Core Data using lazy var for all NSManagedObjectContext, NSManagedObjectModel etc.

So my question is whether to

use the lazy var instantiation approach for creating NSManagedObjectContext, provided that lazy var initiates an object for each thread trying to access (not thread-safe?)

or

declare separate variables for NSManagedObjectContext in each thread and make all thread-related methods to reference two different NSManagedObjectContext provided that lazy var is thread-safe(?) and created only once when it is accessed regardless of thread.

Thank you in advance!

edit: Anyone who is struggling with Core Data concurrency issue, this article lays out a very nice design pattern to work with as pointed out by Aaron in the comment below!

Daniel Shin
  • 5,086
  • 2
  • 30
  • 53

1 Answers1

30

From The Swift Programming Language: Properties:

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

lazy var is not thread safe. You can use

  • dispatch_once (runs once per lifetime of the app)
  • a constant (let)
  • the nested struct pattern (typically used for singletons)

for thread safety. (See this question for some examples.)

You could also employ your own locking using NSRecursiveLock but that's probably not as efficient as dispatch_once.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • Thanks for your answer. So does that mean that `lazy var` will create different instances for different threads trying to access its value? – Daniel Shin Apr 21 '15 at 02:29
  • 1
    No, it means if two threads try to access it at the same time, the second thread might receive a partially initialized object. And since `NSManagedObjectContext` isn't thread safe, you shouldn't access it from different threads. – Aaron Brager Apr 21 '15 at 02:31
  • 1
    Okay. So I should declare different instance of `NSManagedObjectContext` for each thread that I wish to use and enforce methods that execute in background thread to only access `NSManagedObjectContext` that was created in the same background thread previously? Thanks. – Daniel Shin Apr 21 '15 at 02:40
  • Yes, although I'd be surprised if you need more than 1 background thread and 1 main thread context. You can see [this blog post by Marcus Zarra](http://martiancraft.com/blog/2015/03/core-data-stack/) where he describes a good general Core Data setup. – Aaron Brager Apr 21 '15 at 02:42
  • The article was very good. It made my code much more concise and less crash-prone. Thanks for the link! – Daniel Shin Apr 21 '15 at 04:21
  • @AaronBrager Can you provide a link to Apple's official docs that confirms that `lazy` is not thread safe? I couldn't find anything and I actually feell like `lazy` is completely useless if it's not thread safe. :/ – hennes Sep 08 '15 at 12:14
  • @hennes You should assume that nothing is threadsafe unless the documentation specifically says it is. The [properties documentation](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html) describes two cases in which `lazy` is helpful without throwing multithreading into the mix. What's wrong with the options I listed in my answer? – Aaron Brager Sep 08 '15 at 15:15
  • @AaronBrager Sorry, I didn't mean to say there's something wrong with the options you gave. It's just that I had expected it to be threadsafe out of the box because you can't really avoid multithreading in an iOS app. – hennes Sep 08 '15 at 16:42
  • 4
    @AaronBrager A small remark though, dispatch_once uses a static predicate so it'll run once per lifetime of the app. If the lazy var is an instance variable you might want to use the other locking techniques you mentioned or a dispatch semaphore instead. – hennes Sep 08 '15 at 16:45
  • 1
    @hennes there's computational cost associated with locking, so it doesn't make sense for it to be the default. Would be nice to make it an option though (like `lazy threadsafe var`). Also, many `var`s are typically UIKit objects, which must only be accessed on the main thread for the most part. The Swiftier solution, though, is to just pass around immutable value types so you don't need locking. – Aaron Brager Sep 08 '15 at 18:34
  • Do you have any example of how to do this ? – Varun Naharia Mar 15 '17 at 11:39
  • @VarunNaharia Of how to do *what*? – Aaron Brager Mar 21 '17 at 03:37
  • In https://docs.swift.org/swift-book/LanguageGuide/Properties.html, it says: `If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.` – CyberMew Mar 10 '20 at 07:38
  • @CyberMew Good find, I'll add that to my answer. – Aaron Brager Mar 13 '20 at 16:59