Resolved Core data and queuing: processor keeps at 99%

Discussion in 'iOS Programming' started by grandM, Jan 2, 2016.

  1. grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #1
    Hi there

    Can someone explain me why this code forces the processor into overdrive indefinitly?

    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()
    
    
                })
    
               
    
    
            }
    
        }
    
    If I don't do it in a queue, the processor does not go in overdrive.
     
  2. AxoNeuron macrumors 65816

    AxoNeuron

    Joined:
    Apr 22, 2012
    Location:
    The Left Coast
    #2
    You realllllly shouldn't be saving media to Core Data.
     
  3. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #3
    I am using the allows external storage option in CoreData
    I assume it saves an url to that external storage?
     
  4. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #4
    You should be using NSManagedObjectContext's performBlock and performBlockAndWait to ensure that the Core Data stack actions are being run in the correct thread.

    I can't give you any documentation right now, but look them up in Apple's docs and see if that steers you to the right direction.
     
  5. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #5
    I do hope it makes sense trying doing so. I know you said Core Data can't lag, but I'm just hesitant if you go uploading an array of images in Core Data. Then again those image must be shown in the UI. So I'm not sure if it's a bright idea to put them in a thread.
     
  6. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #6
    You are already attempting to do this processing in another thread by placing the activity in the global queue to be processed in the background via GCD. Then you are trying to change the Core Data stack in that thread, which is something you cannot do. Your interaction must in the correct thread, in CD's terms, by using those two functions.

    If there is a lag, the UI should be able to handle it, as you're doing this processing in another thread already and then forwarding those results into the main thread for UI manipulation. If the lag from attempting to use external storage does prove too great, you may find it better to go the other route we discussed in the thread about it.
     
  7. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #7
    So if I understand you correctly you are saying the conversion of the image ought to be done in the global queue. The saving to Core Data should be done in the main_queue?
     
  8. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #8
    No, I am saying that you need to look up PerformBlock and PerformBlockAndWait to properly interact with Core Data. See What is NSManagedObjectContext's performBlock: used for?
     
  9. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #9
    I read it through. If I understood it correctly there are two possibilities.
    1. you work with the moc on the main thread. You do not have to use performblock:. You handle the saving in the main_queue. Your UI elements are updated as they should. The app may slow down from the user viewpoint (not said it will slow down).
    2. You work with a second moc on the global_queue. You must use performblock: or performblock: andwait:. In the case of performblock the application will just move on. In my case the UI elements would not be properly adjusted. So most properly I would need to install some kind of listener. Upon the succesful ending of the block the UI elements are to be updated. In the case of performblock: andwait: the app waits until the second moc has saved the elements to Core Data. This is done in the global_queue. Afterwards I return to the main_queue and the UI elements are updated.
    Is this reasoning correct?
     
  10. Mascots macrumors 65816

    Mascots

    Joined:
    Sep 5, 2009
    #10
    The thread that your ManagedObjectContext sits on is irrelevant as performBlock and performBlockAndWait will safely push any execution within those blocks into the correct corresponding thread. You can decide if you want to handle the processing in a GCD queue or in the thread of your context (which can be private or the main, depending on how you set up your MOC), but in all it is up to you to decide how and where you want to do your processing. The only requirement is that you handle your Managed Object Context changes in a performBlock block.

    For example, wrapping your MOC functions (the self.saveToCoreData()) in a performBlockAndWait block will most likely fix your processing problem. However, while it may fix the processing issue, the method by which you're attempting to do work can be considerably improved by getting a grasp on how Core Data handles concurrency. It's important to understand that when dealing with asynchronous tasks, MOC concurrency != GCD, so you need to read independently on it to understand how to better direct tasks.
     
  11. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #11
    So I've implemented the performBlock. I'm having doubts though. What am I doing? I'm adding photos to a collection view on the screen. Those photos must be added to CoreData and resized. So it seemed logical to me to do so in a thread. The more I think about it the more unsure I'm about it. Basically I have 3 options:
    1. I do it in a private queue. The user adds a photo and to his surprise he doesn't see the photo appear immediately in the gallery. The UI does not stutter.
    2. I do it on the main thread. The photos are added at a fast pace. The UI might become unresponsive.
    3. I do it in a private queue and mimick everything in temporary tables causing a lot of work.
    Following code was my private queue solution but I'm getting an error (This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.) too.

    Code:
    func addGalleryPhotoToCoreDataFromImage(image: UIImage) {
    
           
    
            let (thumbnailImage, bigImage) = self.createThumbnailAndBigImage(image)
    
            let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    
            privateMOC.parentContext = self.managedObjectContext!
    
           
    
            privateMOC.performBlock({ () -> Void in
    
                // 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, 1.0)
    
                self.photoSet!.addObject(newPhoto)
    
               
    
                self.saveToCoreData()
    
                self.refreshItemsInGalleryCollectionView()
    
            })         
    
        }
    
       
    
        func createThumbnailAndBigImage(image: UIImage) -> (thumbnail: UIImage, bigImage: UIImage) {
    
    
                // 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))
    
                return (thumbnailImage, bigImage)
        }
    
     
  12. grandM thread starter macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #12
    You were totally right. Using a second managedObjectContext solved everything. My UI is still responsive and elements are nicely added. I used
    Code:
    NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    
    as UI involved matters ought to be done in this concurrencyType. My sincere thanks!
     

Share This Page