Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,551
309
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.
 
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.
 
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.
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.
 
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.
 
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.
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?
 
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?
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?
 
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.
 
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.
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)
    }
 
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.
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!
 
  • Like
Reactions: Mascots
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.