Your SlideShare is downloading. ×
0
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Adventures in Multithreaded Core Data
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Adventures in Multithreaded Core Data

29,138

Published on

Published in: Technology
1 Comment
18 Likes
Statistics
Notes
  • Good Work! Thanks for sharing your experience.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
29,138
On Slideshare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
182
Comments
1
Likes
18
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Adventures inMultithreaded Core Data CocoaheadsBE - Zoersel, 2012-09-24
  • 2. Introduction
  • 3. Tom Adriaenssen
  • 4. I love...‣ ... my wife‣ ... my 4 kids‣ ... to code‣ ... to play a game of squash‣ ... good beer
  • 5. I open sourced...... some code:‣ IIViewDeckController: “An implementation of the sliding functionality found in the Path 2.0 or Facebook iOS apps.”‣ IIDateExtensions‣ IIPopoverStatusItemSee: http://github.com/inferis
  • 6. I made...... some apps: Butane Drash http://getbutane.com http://dra.sh Hi, @10to1!
  • 7. Butane Campfire client for iOS‣ Official Campfire client kinda sucks, so we rolled our own‣ Somewhat concurrent app‣ Uses Core Data‣ Together with 10to1
  • 8. This presentation is about what I learned while coding Butane.
  • 9. Agenda
  • 10. Agenda‣ Core Data Primer‣ MagicalRecord‣ Concurrency problems‣ Concurrency solutions
  • 11. Core Data Primer
  • 12. What is: Core Data? ‣ Per the documentation: ‣ The  Core  Data  framework  provides   generalized  and  automated  solutions   to  common  tasks  associated  with   object  life-­‐cycle  and  object  graph   management,  including  persistence.http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdTechnologyOverview.html#//apple_ref/doc/uid/TP40009296-SW1
  • 13. Wait, what?
  • 14. What isn’t: Core Data?‣ It’s not an RDBMS.‣ If you want a database and SQL, use Sqlite:
  • 15. What isn’t: Core Data?‣ It’s not just an ORM (Object Relational Mapper)‣ It may look like there’s SQL under the hood, but that’s not necessarily the case.
  • 16. So, what is it then?Core Data provides:‣ persistence‣ change tracking‣ relations (object graph)‣ lazy loading (“faulting”)‣ validation‣ works well with Cocoa (KVO, KVC)
  • 17. Basically:‣ A system to store data‣ Persistence agnostic (local storage, iCloud, AFIncrementalStore, ...)‣ No need to write SQL to query‣ You can keep to Objective-C
  • 18. Your tools:‣ NSPersistentStore‣ NSManagedObjectContext‣ NSManagedObject‣ NSManagedObjectID  ‣ NSFetchRequest‣ NSEntityDescription‣ NSPredicate‣ NSSortDescription
  • 19. A picture says a 1000 words...
  • 20. MagicalRecord
  • 21. MagicalRecord ‣ Writing Core Data code is tedious. ‣ You need quite a bit of boilerplate code to do something simple:NSManagedObjectContext  *moc  =  [self  managedObjectContext];NSEntityDescription  *entityDescription  =  [NSEntityDescription  entityForName:@"Employee"  inManagedObjectContext:moc];NSFetchRequest  *request  =  [NSFetchRequest  new];[request  setEntity:entityDescription];  NSSortDescriptor  *sortDescriptor  =  [[NSSortDescriptor  alloc]  initWithKey:@"firstName"  ascending:YES];[request  setSortDescriptors:@[sortDescriptor]];  NSError  *error;NSArray  *array  =  [moc  executeFetchRequest:request  error:&error];if  (array){   //  display  items  (eg  in  table  view)}else  {        //  Deal  with  error...}
  • 22. MagicalRecord‣ MagicalRecord tries to solve this.‣ = ActiveRecord pattern for Core Data.‣ Encapsulates the tediousness of plain Core Data code.
  • 23. MagicalRecord ‣ Writing MagicalRecord enable code is tedious no more: ‣ That same example is now this:NSManagedObjectContext  *moc  =  [self  managedObjectContext];NSArray  *array  =  [Employee  MR_findAllSortedBy:@"firstname"  ascending:YES  inContext:context];if  (array){   //  display  items  (eg  in  table  view)}else  {        //  Deal  with  error...}
  • 24. Great, isn’t it?
  • 25. MagicalRecord+ write less code+ your code becomes more readable+ good for apps requiring simple storage scenarios (=most apps, probably)+ hides complexity- hides complexity- easy to start, but diving deeper becomes harder- uses defaults that are suboptimal in a multithreaded app- concurrency errors and issues are subtle
  • 26. MagicalRecord‣ used MagicalRecord from the start‣ Took me a while to find out the problems I was having were related to the complexity hiding‣ The defaults are okay only for so much app complexity
  • 27. MagicalRecord‣ That said: Magical Record is great.‣ It will speed up your Core Data development by several factors.‣ Also: take a look at mogenerator: ‣ http://rentzsch.github.com/mogenerator/ ‣ or: brew  install  mogenerator
  • 28. MagicalRecord‣ So I threw it out.
  • 29. MagicalRecord‣ So I threw it out.‣ But not completely.
  • 30. MagicalRecord‣ So I threw it out.‣ But not completely.‣ More about that later.
  • 31. The problems described hereafter only applyto the persistent stores with external backing (for example: sqlite).
  • 32. Concurrency: problems
  • 33. Problems?
  • 34. Problems?‣ Core Data Objects are not thread safe.
  • 35. Problems?‣ Core Data Objects are not thread safe.‣ In essence: you can’t share them across threads (except for NSManagedObjectID).
  • 36. Problems?‣ Core Data Objects are not thread safe.‣ In essence: you can’t share them across threads (except for NSManagedObjectID).‣ Core Data locks objects, even for read operations.
  • 37. Object storage is lockedfor read operations, too‣ Objects used to power the UI must be fetched on the UI thread.‣ Heavy/complex fetch requests (queries) block the UI thread while fetching the objects. You don’t want that.
  • 38. Objects aren’t supposed tobe shared between threads‣ The NSManagedObjectContext “locks” an object when you read one of its properties.‣ This can cause a deadlock when you do access the same data from 2 threads.‣ Reason: faulting support can change the object even while just reading from it.‣ You can’t turn it off.
  • 39. Luckily, we can fix or workaround these problems.
  • 40. Concurrency: solutions
  • 41. Keep to your thread
  • 42. Keep to your thread‣ pre-iOS5: use thread confinement
  • 43. Keep to your thread‣ pre-iOS5: use thread confinement‣ iOS5 and later: use nested contexts
  • 44. Thread confinement‣ In essence: keep an NSManagedObjectContext per thread‣ Be very careful when going from one thread to another.‣ MagicalRecord tries to hide this from you: ‣ It automatically provides a context for each thread ‣ This is a bit counterintuitive since you start mixing objects across threads quite easily.
  • 45. Thread confinement Image source: Cocoanetics
  • 46. Nested contexts‣ Introduced in iOS5‣ Uses Grand Central Dispatch and dispatch queues‣ Core Data manages threading for you‣ Better than thread confinement ‣ more straightforward ‣ more flexible‣ MagicalRecord hides this from you, too. ‣ Automatically switches to dispatch queues on iOS5 even though the API remains the same.
  • 47. Nested contexts Image source: Cocoanetics
  • 48. Nested contexts‣ NSManagedObjectContext = cheap‣ You can nest contexts‣ Each context has its private dispatch queue‣ No manual thread synchronization necessary
  • 49. Queue types ‣ NSConfinementConcurrencyType ‣ The old way (thread confinement) ‣ NSPrivateQueueConcurrencyType ‣ The context has its own private dispatch queue ‣ NSMainQueueConcurrencyType ‣ The context is associated with the main queue (or runloop, or UI thread)parentMoc  =  [[NSManagedObjectContext  alloc]  initWithConcurrencyType:NSMainQueueConcurrencyType];[parentMoc  setPersistentStoreCoordinator:coordinator];moc  =  [[NSManagedObjectContext  alloc]  initWithConcurrencyType:NSPrivateQueueConcurrencyType];moc.parentContext  =  parentMoc;
  • 50. Thread safe? ‣ performBlock:   performBlockAndWait: ‣ Run a block you provide on the queue associated with the context. ‣ Object access in the block is thread safe[context  performBlockAndWait:^{        for  (Room*  room  in  [Room  findAllInContext:context])  {                room.joinedUsers  =  [NSSet  set];                room.knowsAboutJoinedUsersValue  =  NO;                room.unreadCountValue  =  0;                room.status  =  @"";        }        NSError*  error  =  nil;        [context  save:&error];}];
  • 51. performBlock, and wait-­‐  (void)performBlock:(void  (^)())block; ‣ executes the block on the context dispatch queue as soon as possible ‣ nonblocking call ‣ code will not execute immediately-­‐  (void)performBlockAndWait:(void  (^)())block; ‣ executes the block on the context dispatch queue immediately ‣ blocks the current execution until block is done ‣ can cause a deadlock (if you’re already running code on the same queue)
  • 52. When to use what?‣ performBlock: ‣ For actions which are “on their own” ‣ Consider the code in the block a Unit Of Work ‣ Best for save operations ‣ Useful for long fetches (use callbacks)‣ performBlockAndWait: ‣ When you need stuff immediately ‣ Good for small fetches or “standalone” saves
  • 53. How is this better than thread confinement?‣ No manual thread handling, Core Data handles it for you.‣ More flexible: as long as you access managed objects in the correct context using performBlock: you’re pretty safe ‣ also applies to the main/UI thread! (unless you’re sure you’re on the main thread)
  • 54. Saving nested contexts‣ Saves are only persisted one level deep.‣ Parent contexts don’t pull changes from child contexts.‣ Child contexts don’t see changes by parent contexts. ‣ Make them plenty and short-lived
  • 55. How to nest contexts‣ 2 approaches: ‣ root context = NSMainQueueConcurrencyType ‣ root context = NSPrivateQueueConcurrencyType
  • 56. Root = Main ‣ Many child contents with private queues ‣ Root context on main queue ‣ Actual persistence happens on main queue, could block the UI Image source: Cocoanetics
  • 57. Root = Private ‣ Root context with private queue ‣ Many child contents with private queues ‣ context on main queue is child of root ‣ Actual persistence happens in background (does not block the UI) Image source: Cocoanetics
  • 58. What problems did we have again?
  • 59. What problems did we have again?‣ No sharing of NSManagedObjects between threads.
  • 60. What problems did we have again?‣ No sharing of NSManagedObjects between threads.‣ Context locking
  • 61. Sharing: solution‣ Pass only NSManagedObjectIDs to other threads, not objects.‣ Refetch the object on a different thread or queue to work with it.‣ Don’t forget to save the ‘original’ object first before working with it on the second thread or queue.
  • 62. Complex queries‣ Use the same technique for complex or large queries. 1. do the heavy lifting in the background 2. pass list of NSManagedObjectIDs to another thread (e.g. UI thread). 3. load objects as faults, and let Core Data fault them in when you need them (e.g. when accessing a property)‣ That’s lot of requests, but this is actually more performant and desirable in most cases.
  • 63. Locking: solution‣ Use child contexts with their own dispatch queues.‣ Use: performBlock: and performBlockAndWait:  carefully.‣ Deadlocks still possible, especially with performBlockAndWait:
  • 64. A word about NSManagedObjectIDs‣ Two types of IDs: ‣ temporary ‣ when the object hasn’t been persisted to a store ‣ permanent ‣ when the object has been persisted to a store
  • 65. A word about NSManagedObjectIDs‣ Subtle bug: temporary IDs from a non-root context don’t get updated to permanent IDs when saving in the root context‣ The object is saved just fine, but the ID is not updated correctly.‣ When passing these around to other contexts after saving: you won’t find the object in another child context!
  • 66. A word about NSManagedObjectIDs‣ To the rescue: -­‐  (BOOL)obtainPermanentIDsForObjects: (NSArray  *)objects  error:(NSError   **)error;
  • 67. A word about NSManagedObjectIDs -­‐  (BOOL)obtainPermanentIDsForObjects:(NSArray   *)objects  error:(NSError  **)error;‣ Transforms objects with temporary IDs to permanent IDs (through the persistent store of the root context).‣ Do this when creating a managed object and you’re safe.‣ Obtaining permanentIDs is batched, so the performance hit is not that high
  • 68. MagicalRecord‣ I still use MagicalRecord: ‣ Reduced form: no more “automatic” context handling --> dangerous! ‣ Added some extra sauce to work with the nested contexts. ‣ The methods MR supplies still allow for a speedup when coding.
  • 69. Useful References‣ Nested context release notes: http://developer.apple.com/library/mac/#releasenotes/ DataManagement/RN-CoreData/_index.html‣ Magical Record: https://github.com/magicalpanda/MagicalRecord‣ Mogenerator: http://rentzsch.github.com/mogenerator/‣ A good read on Cocoanetics: http://www.cocoanetics.com/2012/07/multi-context-coredata/‣ Core data programming guide: http://developer.apple.com/library/mac/#documentation/ cocoa/Conceptual/CoreData/cdProgrammingGuide.html‣ Butane: http://getbutane.com
  • 70. Thanks for listening.Questions? Contact me:Twitter: @inferisApp.Net: @inferisE-mail: tom@interfaceimplementation.bevCard: http://inferis.org

×