I managed UITableView via NSFetchedResultsController, but all UI blocked(I can do nothing such as tap, scroll...) while NSFetchedResultsController loading data. Is there any solution to resolve this? Thanks!!!
One thing that is important to remember when making your application user friendly is:
If you have any intensive, non-UI related computations on the Main
Thread, you are doing something wrong!
Making the Main Thread UI only will make sure that the UI will never freeze, or as you describe it, get "blocked". Therefore, your NSFetchedResultsController should be on another thread, to ensure that this doesn't happen.
NSOperation's is definitely the way to go for the problem you are describing. Here is a tutorial and sample code which will explain a similar scenario, and how to fix it up.
Hope that Helps!
Related
I'm looking for a way to add a whole load of data to core data while keeping the little activity indicator spinning on the UI. I tried adding the data on another thread but since learned that core data is not thread safe, and I get all kinds of errors. Can anyone suggest another approach to this?
Thanks in advance.
Core Data is not thread-safe, but that just means you have to code appropriately, rather avoiding them entirely. To keep your UI responsive/up-to-date, you'll need to use threads.
Apple's documentation on the subject is here, and this blog post is an excellent walk-through of using multiple threads with Core Data, and some of the pitfalls involved.
You need to use a separate managed object context for each thread. There's some additional work you need to do to make changes from other contexts available to your main thread's context: see Concurrency with Core Data for a full discussion.
Most useful example is a core data XML downloading from iOS sample code. If u don't have access, please let me know and I will write here is a main structure of it.
There is a complex custom delegate techniques from u UIviewController to nsoperation delegate, this is important for u to start showing content immediately to user and show a progress of sync.
In AppStore u can see my "snow IXC" app, where u can see this techniques implemented for indicate user in their UIviewController about progress. It's free for downloading.
This is how I avoid background threads with a loading indicator (I use DSActivityView but this should work with other implementations):
In your code when you are going to be displaying the indicator run all the code to show the indicator first. After that code have a separate method call to do all the loading work. Call it by using the method:
[self performSelector:#selector(loadMethodName) withObject:nil afterDelay:0.0];
Normally the app will go straight into the loading code without waiting to show the indicator view. By calling it this way it will first finish displaying the indicator before it moves into the loading code.
I'm a pretty comfortable CoreData user. I had a problem that was absolutely killing me, figured out what was going wrong and have NO CLUE why it was causing the problems I was seeing. Can anyone explain this?
Okay, we have a main MOC that does most the 'read-only' data work and handles simple write operations. When we're about to do a big write/import/etc, we allocate a background MOC on a new thread, do all the heavy work, then save it. Saving causes our NSManagedObjectContextWillSaveNotification to fire and then we mergeChangesFromContextDidSaveNotification: and everything on the main MOC reflects the changes. Pretty standard.
So a few days ago a change was made in one of the NSManagedObject subclasses. The change was a call in setValue:forKey:. When no special handling was done in that method, we were originally calling [super setValue: forKey:]. It was changed to [self setPrimitateValue: forKey:] and...
After a save the mergeChangesFromContextDidSaveNotification: would get called, but the main MOC wouldn't reflect the changes. So that's a bit weird. But close the application and then opening it again, the changes would magically BE THERE. What!?!
I know it's some problem with the persistant store saving the changes but not the other MOC. Then when the new MOC gets created new launch, everything is in sink again. Why?
Any light on this topic would help a bunch.
setPrimitiveValue:forKey: doesn't cause all the kvo notifications to fire. I think this is the cause of your problem. You probably need to wrap it around willChangeValueForKey: and didChangeValueForKey: calls.
I have two uiviewcontrollers. I am using NSThread in first viewcontroller and its works perfect.. Some times the thread was running in the firstviewcontroller, that time i Want to go Secondviewcontroller.This action stops my thread of firstviewcontrollr..
How could i fix this... Can anyone help me?
Thanks in advance
It would be good if you could clarify the question, because it's hard to understand exactly what you want to do and what the problem is. In general, UI code should always run in the main thread. That is clearly stated in the Cocoa documentation, and you will not succeed in having several threads trying to control the UI.
If you want to run code in other threads and have them influence the UI, you need to implement mechanisms to communicate information to the main thread. One method that comes in handy is NSObject's
performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
If you want to use notifications, you must dispatch them on the main thread also. It is easy to get confusing bugs if you don't stick to this principle (although things might seems to work at first).
I'm going to do some sophisticated things in the delegate's methods during scrolling. I'll also implement the dynamic preloading of contents, so that it's theoretically possible to scroll through a few hundret thousand images. But I fear, that every time I do the preloading action for the next big chunk in scrolling direction, the delegate will wait for the data source to deliver the data, and the whole scroll view will be freezed for that moment.
The problem is, that I need always access to the subviews of the scroll view. I'm new to the platform and I don't know if I would still have access when I open up another thread for that preloading actions? Or would the scrollview not wait for the delegate to get things done?
I'm in the planing phase, so haven't implementet much jet.
You can only use UI classes from the main thread. So what you should do is to compute as much as possible in a background thread (I believe you can load your images in a background thread too), and then use performSelectorOnMainThread:withObject:waitUntilDone: to manipulate UI classes on the main thread.
See How do I update the UI in the middle of this thread? for the another instance of your question
I don't have a specific answer, to your specific question. I just want to make sure you are aware of the ~25MB limit of RAM for your app. I can't give links on this, because even though Apple knows the limit, they aren't telling.
I'm drawing offscreen to a CGContext created using CGBitmapContextCreate, then later generating a CGImage from it with CGBitmapContextCreateImage and drawing that onto my view in drawRect (I'm also drawing some other stuff on top of that - this is an exercise in isolating different levels of variability and complexity).
This all works fine when it's all running on the main thread. However one of the motivations for splitting this out this way was so that the offscreen part could be run on a background thread (which I had thought should be ok since it's not rendering to an onscreen context).
However, when I do this the resulting image is empty! I've checked over the code, and placed judicious NSLog's to verify that everything is happening in the right order.
My next step is to boil this down to the simplest code that reproduces the issue (or find some silly thing I'm missing and fix it) - at which point I'd have some code to post here if necessary. But I first wanted to check here that I'm not going down the wrong path with this. I couldn't find anything in my travels around the googlesphere that sheds light either way - but a friend did mention that he ran into a similar issue while trying to resize images in a background thread - suggesting there may be some general limitation here.
[edit]
Thanks for the responses so far. If nothing else they have told me that at least I'm not alone in not having an answer for this - which was part of what I wanted to find out. At this point I'm going to put the extra work into getting the simplest possible example and may come back with some code or more information. In the meantime keep any ideas coming :-)
One point to bring up: A couple of people have used the term thread safety with respect to APIs. It should be noted that there are two types of thread safety in this context:
Threadability of the API itself - ie can it be used at all from more than one thread (global state and other re-entrancy issues such as C's strtok are common reasons that an API might not be thread safe too).
Atomicity of individual operations - can multiple threads interact with the same objects and resources through API without application level locking?
I suspect that mention so far has been of the first type, but would appreciate if you could clarify.
[edit2 - solved!]
Ok, I got it all working. Executive summary is that the problem was with me, rather than bitmap contexts themselves.
In my background thread, just before I drew into the bitmap context, I was doing some preparation on some other objects. It turns out that, indirectly, the calls to those other objects where leading to setNeedsDisplay being called on some views!
By separating the part that did that out to the main thread it now all works perfectly.
So for anyone who hits this question wondering if they can draw to a bitmap context on a background thread, the answer is you can (with the caveats that have been presented here and in the answers).
Thanks all
Just a guess, but if you are trying to call setNeedsDisplay from another thread, you need to call it via performSelectorOnMainThread instead.
What you're doing should work if you're working with the CGContextRef in one and only one thread. I've done this before with 8 cores working on 8 different parts of an image and then compositing the different resultant CGImageRefs together and drawing them onscreen.
Apple don't say anything about thread safety on iPhone but Cocoa (as opposed to UIKit) is generally thread safe for drawing. As they share a lot of drawing code, I would assume drawing on iPhone is threadsafe.
That said, your experience would imply there are problems. Could it be that you are using your image before it is rendered?
Not all APIs are thread-safe. Some require locking or require that they be run on the main thread. You may want to scour the documentation. I believe there is a page that summarizes which parts of the SDK are thread-safe and which aren't.
In case anyone is/was searching for exactly how to do this i've written a blog post that describes how to do this, and wraps the whole thing in a NSOperation subclass.