nsset or nsorderedset for Core Data

Discussion in 'iOS Programming' started by grandM, Dec 28, 2015.

  1. grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #1
    Normally Core Data uses a NSSet. If the order matters though one could opt for an NSOrderedSet. I was wondering what would be faster:
    - uses NSSet and add an ID to each element. Use NSPredicate to sort them. Adding elements would require altering the ID of some elements.
    - use NSOrderedSet. Unsure how an element is inserted at a specific row in Core Data however when the set is ordered.
     
  2. Mascots, Dec 28, 2015
    Last edited: Dec 28, 2015

    Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #2
    They promise more than they deliver. Here are some of the reasons I have avoided NSOrderedSets in the past:

    - Only a single index to sort items on per relationship (what if we wanted entities to be in a multiple lists, dependent on parent entities, and have each list retain it's own order? You'd have to undo all of your Ordered Set Logic)
    - With the above point, that index isn't exposed like a property would be, so using it along side something like NSFetchedResultsController is impossible (see here).
    - To avoid auto-generation errors (though if you've been using Swift you've undoubtedly ran into auto generation issues already), which is a bane of Xcode's existence.
    - Ordered sets are not actually Sets (no inheritance from NSSet), which causes problems for those who don't anticipate that exception.

    In pretty much any situation, I'd suggest a NSSet and then use a dedicated property/column or external relationship to a separate table for managing order.
     
  3. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #3
    Yes I was just converting it to an NSOrderedSet and was running into problems. Problem is I have a many-to-many relationship between A and B. So if I add an id to B that would solve my ranking if I tap on an instance of A. Not sure though if I won't run into problems as an instance of B can be used for several instances of A.

    Talking about Core Data. Do you persist the NSSet immediately or do you first perform your logic on a local array?
     
  4. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #4
    I would suggest having a mutual table which maintains the order for all of the B objects that are children of their appropriate A objects.

    Avoid saying "persist" in Core Data for a case like this, since it implies that you're writing to your persistent store (like the database). I think the correct word would be "commit", since a Managed Object Context won't be guaranteeing that its commit will save the data - that's not really its responsibility - instead you are guaranteeing that all of the references to that object in the current context are synchronized and pushed up the stack one level. The distinction is important when working with multiple contexts and when understanding how Core Data splits the responsibility among its parts.

    In most situations, I hold to commit my changes to a NSSet until I am ready for the data to be seeded back to the rest of the references of that object in the current context. Also: I'll do my best to do as much of the work directly on the entity, rather than indirectly through the relation, which can be funky or awkward code.
     
  5. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #5
    ok, so I ought to work with a temporary array or something like that and commit it only when needed. I was just doing some tests. I guess if I ought to use the order in Core data I could use a temporary NSMutableOrderedSet. I have to admit I probably will use the NSSet solution, using one or more predicate to sort the data into a temporary array. One of the things bothering me about using the ordered solution is the day you want to use an external DB. In that case some of the logic got lost. One thing is for sure: the @addObject() does not seem to work using the ordered sets. Seems to be the case since 2011 :s
     
  6. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #6
    Then again is I ought to go ordered sets and would not use the @addObject() it is pretty simple to add elements. I just read this in a book.
    Code:
    let walks = currentDog.walks!.mutableCopy() as! NSMutableOrderedSet
    
    walks.addObject(walk)
    currentDog.walks = walks.copy() as? NSOrderedSet
    
    
    That addObject can also be insertObject(object, atIndex: ). Would be pretty easy to add elements at the proper place.
     
  7. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #7
    If you want to use NSOrderedSet, you'll have to write your own setter and getting or modify the broken one that Apple supplies - auto generation of NSManagedObject subclasses hasn't worked for years for me, so it's something I've moved passed complaining about.

    IMO, my biggest beef, which is what you hinting at, is that the order is single and not properly exposed. That lack of exposure makes working with it, especially alongside Core Data API's, way harder than it should be, or just flat out impossible.
     
  8. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #8
    So it would be better just to insert an ID and use a NSSet?
     
  9. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #9
    Oh for sure, I will always advocate that way - There are very few situations in which using a NSOrderedSet would be useful and wouldn't come back to bite you with changes to your model layer (or UI) in the future.
     
  10. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #10
    I'm in doubt about something else now. I had made a Gallery of GalleryItems. This GalleryItem now coincides with a Photo object in my CoreData. Am I correct I could as well work with an array of Photo now? I'm amongst others thinking of the creation of a new Photo object. If I use a GalleryItem I can create it immediately and use a thread to put it into Core Data. If I use the Photo I must create it in CoreData. Would that make the app slower? Do this GalleryItems still serve a purpose now or should I work directly with Photos (being the Core Data object)?
     
  11. Mascots, Dec 28, 2015
    Last edited: Dec 28, 2015

    Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #11
    If I understand you correctly, you have the following entities: Gallery, GalleryItem, and Photo.

    Ideally, I would setup things like:

    Entity: Gallery
    Properties: Title, Description
    Relationships: To-many -> GalleryItem
    -
    Entity: GalleryItem:
    Properties: Order, AddedOn
    Relationships: To-one -> Gallery; To-one -> Photo
    -
    Entity: Photo
    Properties: Path, Title, CreatedOn
    Relationships: To-Many -> Gallery Items

    This is known as the "Toxi" solution (at least when implementing tagging, but the solution it proposes is relevant here). Using this pattern, you are able to use GalleryItem to bridge your two nodes of data (Gallery and Photo).

    Bringing us back to the topic this entire thread is about: a benefit of using this pattern over directly setting Photo in a one-to-many relationship with Gallery is the ability to set metadata on the relation (in this case, the order and addedOn properties) independently of the Photo and Gallery entities, but exposed and directly attached to a relevant entity. This method means you can take advantage of lower-level database sorting and higher level classes like FRC when handling data.

    When working in a context like Core Data on the iOS or OS X, your connections are most likely limited to a single client, and as long as your entities properly indexed, you won't likely see a performance hit by needing to hop relationships to gather the necessary data. This method is pretty robust and well established, as well as used in a variety of contexts. I suggest looking into peoples' usage with it and other database patterns to familiarize yourself and see others' experiences.
     
  12. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #12
    It's close. At the moment A Gallery can have several GalleryItems. A GalleryItem consists of a photo and indeed the order in the Gallery. This GalleryItem was defined as a local Class and not an Entity hence not commited to Core Data. The purpose was to give the user the impression the photos were sorted. In fact they ought to be sorted using the sorted option on CoreData. I changed it to not sorted though hence a NSSet. During one session the photos kept ordered on the View using the order in this local Class. But you're right if I want the photos to keep the same order I ought to activate the ordered option or insert an extra Entity called GalleryItem.

    Would you keep the counterpart of the galleryItems too? If I change everything to entities I have the problem that a user could get the impression the app lags. If I use classes for the view and fire a commit at CoreData then the user won't notice the lag? Or is this lag neglectable?
     
  13. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #13
    In this regard, I can almost completely ensure that you will not experience any sort of lag.

    In a standard environment, the chance that you'll hit any sort of performance cap while transversing a relationship using Core Data is none.
     
  14. grandM, Jan 2, 2016
    Last edited: Jan 2, 2016

    grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #14
    Problem is that I'm also converting images. So I'm inclined to put this conversion in a queue. However I'm reading that this can cause a problem? I was going to put up a queue and convert the images. In the queue I was going to save to core data. Meanwhile I was going to use the GalleryItems (which at the moment are a copy of the photos) and work on the local class to appear in the UI. Hence removing the chance of a lag due to the conversion.

    At the moment I have this function. Could this result into a problem? I'm using the same managedObjectContext I'm always using.

    Code:
       func addGalleryPhotoToCoreDataFromImage(image: UIImage) {
    
          
    
            dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { () -> Void in
    
              
    
    
                // create a thumbnail and a smaller photo
    
                let thumbnailImage = self.resizeImage(image, targetSize: CGSizeMake(167, 167))
    
                let screenSize : CGSize = UIScreen.mainScreen().bounds.size
    
                let bigImage = self.resizeImage(image, targetSize: CGSizeMake(screenSize.width, screenSize.height))
    
              
    
                // save thumbnail and photo to CoreData
    
                let newPhoto = NSEntityDescription.insertNewObjectForEntityForName("Photo", inManagedObjectContext: self.managedObjectContext!) as! Photo
    
                newPhoto.thumbnail = UIImageJPEGRepresentation(thumbnailImage, 0.1)
    
                newPhoto.photo = UIImageJPEGRepresentation(bigImage, 0.9)
    
                self.photoSet!.addObject(newPhoto)
    
              
    
                self.saveToCoreData()
    
                dispatch_async(dispatch_get_main_queue(), { () -> Void in
    
                    self.refreshItemsInGalleryCollectionView()
    
    
                })
    
              
    
    
            }
    
        }
    
     

Share This Page