reload uitableview simultaniously - iphone

i have to reload uitableview simultaniously using a thread. i'm already using two threads for loading data from web. Is it possible using a thread for reloading tableview? Is there any other way?

Simultaneous to what? The reloading takes time and you need to reload the backing data model in background, while still displaying being able to display data to the user?
If that is the case, then I would:
Define the data model as property.
Update a temporary data model in a background thread.
When updated I update the data model property on the main thread.
It is important to do the update of the real model property, and request of reloading data for the table view on the main thread. Otherwise there will be a time slot where the table view can request a view for a data model item that is no longer available.
An implementation would be something like this:
-(void)releadData;
{
[self performSelectorInBackground:#selector(reloadDataInBackground)
withObject:nil];
}
-(void)reloadDataInBackground;
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
MyDataModel* model = nil;
// Do what is needed to setup model.
[self performSelectorOnMainThread:#selector(updateModelOnMainThread:)
withObject:model
waitUntilDone:NO];
[pool release];
}
-(void) updateModelOnMainThread:(MyDataModel*)model;
{
self.model = model;
[self.tableView reloadData];
}

simultaneously with what?
everything UIKit has to be done on the main thread, so you can use background threads to talk to the internet or to do your own processing, but any actual interaction with a UITableView has to be on the main thread.

Related

Obj-C, failed to launch in time, doing some DB processing which is taking 20 seconds, advice?

Application Specific Information:
com.my-app failed to launch in time
Elapsed total CPU time (seconds): 20.090 (user 20.090, system 0.000), 100% CPU
Elapsed application CPU time (seconds): 17.598, 87% CPU
I've made a modification to my app and as a result I now run a function from applicationDidFinishLaunching which will do some database processing.
I'm basically creating some new records and updating some existing ones.
For one of my existing beta testers / real customers, this is taking 20 seconds to complete.
Although in this case this is a one off, users could experience this situation if they haven't used the app for a while.
Normally the process wouldn't take long at all, as there would only be a few transactions to process.
I'm unsure how to proceed, any suggestions ?
I suggest you to do your db processing in the background. Maybe you could disable the interface or display a waiting indicator while you are updating the db in the background thread. Then, once finished you could enable the interface or hide the indicator.
There are different ways to create background thread.
Create a thread manually using NSThread class
Using NSOperation and NSOperationQueue classes
Using Grand Central Dispatch (GCD)
Hope it helps.
Edit
Here simple code for your goal (following #JeremyP suggestion).
First, create a NSOperation subclass
// .h
#interface YourOperation : NSOperation
{
}
//.m
#implementation YourOperation
// override main, note that init is executed in the same thread where you alloc-init this instance
- (void)main
{
// sorround the thread with a pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do your stuff here..
// you could send a notification when you have finished to import your db,
// the notification is sent in a background thread,
// so in the place where you listen it, if you need to update the interface,
// you need to do it in the main thread (e.g. performSelectorOnMainThread)
[[NSNotificationCenter defaultCenter] postNotificationName:kImportComplete object:self];
[pool drain];
pool = nil;
}
Then, in your application delegate for example call [self import]; that could be defined as follow:
if (!(self.operationQueue)) {
NSOperationQueue* q = [[NSOperationQueue alloc] init];
[q setMaxConcurrentOperationCount:1];
self.operationQueue = q;
[q release];
YourOperation *op = [[YourOperation alloc] init];
[self.operationQueue addOperation:op];
[op release], op = nil;
}
I would (and have in the past in a few apps) perform the DB update on a background thread and show the user a 'please wait' screen while the updates complete.

UITableView datasource methods called before database open

I am writing an application that uses a Core Data database to store data which I then want to display using a UITableView. I have everything working, but was a little curious if there is a way around one small point that is bugging me...
When the app runs I do the following:
Create a NSManagedDocument
Create a NSFetchedResultsController
Open the Core Data database.
I am using ...
[[self testDatabase] openWithCompletionHandler:^(BOOL success) {
if(success) {
...
}
}];
to open the database but my problem is that by the time the block has executed the UITableView dataSource has already called -[TableViewController tableView:numberOfRowsInSection:] and returned rows=0
My solution so far has been to ignore this first "automatic" call and instead add a performFetch and a reloadData to the block that executes when the database is open.
[[self testDatabase] openWithCompletionHandler:^(BOOL success) {
if(success) {
[[self fetchedResultsController] performFetch:nil];
[[self tableView] reloadData];
}
}];
My question, is there a way to stop or delay that first call? or is there something I should add to -[TableViewController tableView:numberOfRowsInSection:] to manage that first call, or does it simply not matter and its fine as it is?
You can try starting with nil for tableView.dataSource and setting it after fetch.

Core data saving in thread

I'm developing a client server application in iPad. I need to save quite a number of data the server sends me. it sends me a long string, and i have to break it up into small records and save it in core data. it sends me a total of probably 20 messages and each messages has roughly 100 over records.
The problem now is the user has to wait for all the messages to be saved into the core data, before the UI unfreezes, as its all running in the main thread.
Question, Can i receive the message from server, and throw the breaking up of data and saving into core data into threads? i keep getting the sigbart error when the context is save. I checked the core data, it saves about 4 records before hitting that error.
Can multiple threads access / save into core data at the same time?
Sorry i'm really lost. tried the open source Magical Records but it keep having errors.
A Core Data managed object context is not thread safe. While you can have a single Core Data store, you need to create a separate managed object context for each thread. If you need to pass references to managed objects between threads, you need to pass the object ID and then read the object from the local managed object context, rather than trying to pass the object itself.
Doing this will enable you to save in the background using Core Data.
Beware when saving on background threads however that the app could exit before the background thread is finished saving. See this discussion.
Since Core Data requires one Managed Object Context per thread, a possible solution would be to track a context per Thread in a global manager, then track save notifications and propagate to all Threads:
Assuming:
#property (nonatomic, strong) NSDictionary* threadsDictionary;
Here is how to get the managed object (per thread):
- (NSManagedObjectContext *) managedObjectContextForThread {
// Per thread, give one back
NSString* threadName = [NSString stringWithFormat:#"%d",[NSThread currentThread].hash];
NSManagedObjectContext * existingContext = [self.threadsDictionary objectForKey:threadName];
if (existingContext==nil){
existingContext = [[NSManagedObjectContext alloc] init];
[existingContext setPersistentStoreCoordinator: [self persistentStoreCoordinator]];
[self.threadsDictionary setValue:existingContext forKey:threadName];
}
return existingContext;
}
At some point in the init method of your global manager (I used a singleton):
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(backgroundContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:nil];
Then to receive save notifications and propagate to all other managed context objects:
- (void)backgroundContextDidSave:(NSNotification *)notification {
/* Make sure we're on the main thread when updating the main context */
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(backgroundContextDidSave:)
withObject:notification
waitUntilDone:NO];
return;
}
/* merge in the changes to the main context */
for (NSManagedObjectContext* context in [self.threadsDictionary allValues]){
[context mergeChangesFromContextDidSaveNotification:notification];
}
}
(some other methods were removed for clarity)

iPhone SDK SSCollectionView load item from thread

I have an SSCollectionView which is modeled after NSCollectionView and UITableView. It calls a function to load each item at an indexPath.
I want to asynchronously load images into each one of those paths.
- (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath {
....
[self startLoadingItem:item indexPath:indexPath];
return item;
}
-(void)startLoadingItem:(SSCollectionViewItem *)collectionViewItem indexPath:(NSIndexPath *)indexPath
{
[SEThreadInvocation detachNewThreadSelector:#selector(loadItem:indexPath:) toTarget:self withObjects:collectionViewItem,indexPath, nil];
}
SEThreadInvocation is just a subclass of NSThread so that I can send more than one object to a selector.
-(void)loadItem:(SSCollectionViewItem *)collectionViewItem indexPath:(NSIndexPath *)indexPath {
...
collectionViewItem.imageView.image = imageToDisplay;
}
What is happening is that the code initially works, but every time the collection reloads (often) it puts images all over the collection view randomly.
I believe this is because my SSCollectionViewItem is pointing to a different memory location each time and my item is not thread safe, so it is just stamping my images all over the place when they change in memory.
Also, I thought it might be replacing the images in memory, so I replaced it with numbers and the numbers bounce all over the collection view when reloading as well.
How can I make my objects thread safe in this context?
SSToolkit has a sample Catalog project which has a SSCollectionView demo. It loads images in the way you are needing.

Update and delete entities in two different process

I'm working on an ipad application that use coredata. It download information on a database that is on the web, and record them in coredata. The application is based on a split view. My problem was to make the update of the data in background. Here is how I've done :
- I've create an NSOperation, that does the download and the update of the data.
- This NSOperation use a different NSManagedObjectContext than the context of the appDelegate, return by this function, that is in the appDelegate :
(NSManagedObjectContext*)newContextToMainStore {
NSPersistentStoreCoordinator *coord = nil;
coord = [self persistentStoreCoordinator];
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:coord];
return [moc autorelease];
}
- I've had an observer in the NSOperation, that will call this function in the appDelegate when I save the context, to modify the context of the delegate too :
- (void)mergeChangesFromContextSaveNotification:(NSNotification*)notification {
[[self managedObjectContext]mergeChangesFromContextDidSaveNotification:notification];
}
But I've a problem : when I delete an element in the rootViewController, I really don't know how to manage the changes in the background process, because there is a loop inside that browse all the entities : if I delete one entitie when the background loop is at the same entitie, this is... really bad...
My solution was just to stop the update process when I delete an entitie, and then restart it, simply... But I've realised that the changes made in the "main" context were not apply in the new context I just create for the update.
So I ask you : Why the changes aren't apply in the new context ? If this the wrong way, how do you do this ? Using mergeChangesFromContext or something else ?
Thanks you in advance.
Sorry, my mistakes :
- First, my entities contained other entities, and because of a bad relationship, the entities that was contained in the parent entitie weren't deleted.
- Second, I was thinking that call -cancelAllOperations will stop the current operation, but it's not, you have to check in the nsoperation if the process is cancelled with [self isCancelled].
That's all !
U don't need to take care if somebody will delete something from interface, bcs NSArrayController is thread safe. But if u make updates and delete in same time, u have to take care about it.