Table view freezes when sql queries are fired in thread - iphone

I have attached a new thread and in that thread I'm firing 5 SQLite queries.
Problem is that until execution of all my queries is finished, I'm not able to scroll the table view. It freezes for some seconds.
-(void)viewDidAppear:(BOOL)animated
{
[NSThread detachNewThreadSelector:#selector(GetBackEndData)
toTarget:appDelegate withObject:nil];
}
// this is in appDelegate
-(void)GetBackEndData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if([dicBarnearMe_Detail count]==0)
{
// this are sql queries method.
[appDelegate SelectBeersonbottle_Count];
[appDelegate SelectBeersonTap_Count];
[appDelegate SelectGrowler_Count];
[appDelegate SelectHappyHours_Count];
[appDelegate SelectEvents_Count];
// After completing this process I'm post notification
// for reloading table in other controller.
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload"
object:nil userInfo:nil];
}
[pool release];
}

You are making a new thread in viewDidAppear and executing the GetBackEndData selector in separate thread.
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload"
object:nil userInfo:nil];
you making NSNotificationCenter in GetBackEndData and reloading some data that means you have to wait until your thread complete the execution and blocking UI thread.
making a thread in viewDidAppear is not a right approach you can use a dispatchQue in some other function or alternative option is wait until your thread execution get completed and show activity Indicator.

Related

applicationDidEnterBackground and applicationWillEnterForeground method are not called when pressed home button in iOS simulator

I need a long running task to be done in background as well as in foreground. This updates the core data. So to maintain UI responsive I created an another thread where I use different managedObjectContext(MOC). So a timer is set in background as well as in foreground and is inactivated appropriately when state changes. Before the task is starting and after the task is completed when I press home button it calls the two delegate methods properly but during the task is active when I press home button screen changes and UI hangs (becomes blank) but the two delegate methods are not called properly and the app is not terminated. I could not find the reason why this happens so. It would be helpful if someone can help.
I will attach the required code with this :
-(void) startTimerThread
{
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Add code here to do background processing
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:[(AppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]];
self.managedObjectContext = context;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:context];
NSLog(#"managedObjContext : %#\n",self.managedObjectContext);
[self getDataFromFile];
dispatch_async( dispatch_get_main_queue(), ^{
// Add code here to update the UI/send notifications based on the
// results of the background processing
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
[context release];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:context];
});
});
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Background\n");
[self.notificationTimer invalidate];
self.notificationTimer = nil;
UIApplication *app = [UIApplication sharedApplication];
self.bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//start location update timer and background timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:180 target:self
selector:#selector(startLocationServices) userInfo:nil repeats:YES];
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
self.backgroundTimer = [NSTimer scheduledTimerWithTimeInterval:90 target:self.logDownloader selector:#selector(getDataFromFile) userInfo:nil repeats:YES];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Foreground\n");
//invalidate background timer and location update timer
[self.timer invalidate];
[self.backgroundTimer invalidate];
self.timer = nil;
self.notificationTimer = nil;
self.logDownloader.managedObjectContext = self.managedObjectContext;
NSLog(#"managedObjContext : %#\n",self.logDownloader.managedObjectContext);
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReloadAppDelegateTable" object:nil];
self.notificationTimer = [NSTimer scheduledTimerWithTimeInterval:180 target:self.logDownloader selector:#selector(startTimerThread) userInfo:nil repeats:YES];
}
On iOS13+, if you implement UIWindowSceneDelegate, it calls func sceneDidEnterBackground(_ scene: UIScene), instead.
The reason why applicationDidEnterBackground: and applicationDidEnterForeground: are never called is because these methods are used in joint with Application does not run in background this option can be found in your ***-info.plist. If this option is set to YES than your app will never call these methods, because these when you press the home button with an app that has set the option to YES the instance of the app that is running will get terminated so everytime you press the home button and then select the app icon a new instance is being created so it is using applicationWillTerminate:.
The methods that Kirti mali has said would also be the incorrect methods to use for want you are after, the reason being is that applicationDidBecomeActive: and applicationWillResignActive: are used when something like when you answer a phone call. The instance running is not terminated neither is it sent to the background. The instance is paused until the user has finished on that call when it will become active again.
So the solution to this would be if you want the app to run in background would be to change the option "Application does not run in background" in the ***-info.plist to be NO just applicationDidBecomeActive: and applicationWillResignActive: is the wrong way for these methods to be used.
Please see the apple documentation on UIApplicationDelegate to get a better understanding of these methods.
Since your App runs on background those methods will never called however you can use willEnterForegroundNotification and didEnterBackgroundNotification to do same thing you want.
You can write following codes inside the didFinishLaunchingWithOptions method of ApplicationDelegate
NotificationCenter.default.addObserver(forName:UIApplication.willEnterForegroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
NotificationCenter.default.addObserver(forName:UIApplication.didEnterBackgroundNotification, object: nil, queue: nil) { (_) in
// Your Code here
}
This method is called when home button is pressed
- (void)applicationDidBecomeActive:(UIApplication *)application
and this method is called when icon button is pressed
- (void)applicationWillResignActive:(UIApplication *)application

Memory crash updating UIActivityIndicatorView

Whenever I am trying to update the UIActivityIndicatorView from a thread, the app is getting crashed by throwing an exception
modifying layer that is being finalized - 0x7e177fd0
-[CALayer removeAnimationForKey:]: message sent to deallocated instance 0x7e177fd0
When I try tracking the memory leaks form the mallocDebugger tool, this crash is not happening at all the time happening 1 out of 10
please help me out from this memory issue
Thread implementation:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
[autoRechargeCell addSubview:activityIndicator];
[self.activityIndicator startAnimating];
if( [PennyTalkAPI getBalanceInfoForAccount:appDelegate.accountNumber withPIN:appDelegate.pinNumber])
{
[autoRechargeCell.switchField setOn:[[NSUserDefaults standardUserDefaults] boolForKey:#"AutoRecharge"]];
[self.activityIndicator stopAnimating]; <<<<<<<<<<<<<<<<<<<<<<
}
else
{
[self.activityIndicator stopAnimating];
}
[pool release];
This is the code I have written.
See this.
add [[NSNotificationCenter defaultCenter] removeObserver:self]; to UIActivityIndicatorView+AFNetworking.m dealloc
https://github.com/AFNetworking/AFNetworking/issues/2748
You are updating it from secondary thread that why it is crashing call this on main thread like
[self.activityIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
I think it may help you ....

How to cancel or stop NSThread?

I'm doing an app that loads the contents of viewControllers using NSThread while is reading an XML file.
I have it done as follows:
-(void)viewDidAppear:(BOOL)animated
{
// Some code...
[NSThread detachNewThreadSelector:#selector(loadXML) toTarget:self withObject:nil];
[super viewDidAppear:YES];
}
-(void)loadXML{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Read XML, create objects...
[pool release];
}
My problem is that I don't know how to stop the NSThread if the user changes to another viewController while the NSThread is loading, doing that the app crashes.
I've tried to cancel or exit the NSThread as follows but without success:
-(void)viewsDidDisappear:(BOOL)animated{
[NSThread cancel];
// or [NSThread exit];
[super viewDidDisappear:YES];
}
Can anyone help? Thanks.
When you detach new thread, you can no more cancel or exit it from viewDidDisappear etc. These UI specific methods execute only on main thread so the exit/cancel applies to the main thread which is obviously wrong.
Instead of using the detach new thread method, declare NSThread variable in .h and initialize it using initWithTarget: selector: object: method and cancel it whenever/wherever you want to..
you can also use [NSThread exit]; method of NSThread.
It's better to let a thread end gracefully, i.e. reach its natural conclusion, if you can. It sounds like in your case you can afford to. Also be sure that you're updating the user interface from the main thread, not a secondary thread, as UIKit is not thread safe.
You wrote:
... the app stops responding while the thread finishes...
Once you flag a thread for cancelling or exit, you have to manually stop whatever the thread was called to do. An example:
....
- (void) doCalculation{
/* Do your calculation here */
}
- (void) calculationThreadEntry{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSUInteger counter = 0;
while ([[NSThread currentThread] isCancelled] == NO){
[self doCalculation];
counter++;
if (counter >= 1000){ break;
} }
[pool release]; }
application:(UIApplication *)application
- (BOOL)
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
/* Start the thread */
[NSThread detachNewThreadSelector:#selector(calculationThreadEntry)
toTarget:self withObject:nil];
// Override point for customization after application launch. [self.window makeKeyAndVisible];
return YES;
}
In this example, the loop is conditioned on the thread being in a non-cancelled state.

how to properly use autoreleasepool for an nsoperationqueue

I have an app that I am refactoring and I just implemented multithreading so that the UI may run smoother. In the iphone simulator I don't get any leaks but testing on my iPhone 3G running on iOS 4.2 I get a memory leak. I have done a lot of searching for the correct way to implement an autoreleasepool with an operationqueue, help will be greatly appreciated.
I have created an nsoperationqueue in my viewcontroller as such
- (void)loadData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSOperationQueue *queue = [NSOperationQueue new]; // creates multithread for loading data without slowing UI
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(firstRun) object:nil];
[queue addOperation:operation];
[operation release];
[queue release];
[pool release];
}
First, you shouldn't just create and then release the queue. It's more natural to create that queue as one of your class's ivars and then release it when your view controller goes away (you can also cancel up any pending operations and cancel/wait for any running operations to complete).
Second, you don't need the autorelease pool in the method that creates the operation and adds it in the queue since that method is being called from the main thread. Instead, you need the autorelease pool from the method your object actually calls (this is what may be running on another thread).
So, you might have the following (assuming you name your queue ivar queue_):
- (void)viewDidLoad
{
[super viewDidLoad];
if( !queue_ ) queue_ = [[NSOperationQueue alloc] init];
// other view loading code
}
- (void)loadData
{
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(firstRun) object:nil];
[queue_ addOperation:operation];
[operation release];
}
- (void)firstRun
{
// Here we may run on another thread, so 1) we need an autorelease pool; and
// 2) we need to make sure we don't do anything that requires a runloop
NSAutoreleasePool* threadPool = [NSAutoreleasePool new];
// do long-running things
[threadPool drain];
}
- (void)dealloc
{
if( queue_ ) {
[queue_ setSuspended:YES];
[queue_ cancelAllOperations];
// you need to decide if you need to handle running operations
// reasonably, but don't wait here because that may block the
// main thread
[queue_ release];
}
// other dealloc stuff
[super dealloc];
}
You could also initialize your queue on demand, so instead of initializing in viewDidLoad, check for its existence and initialize if necessary anywhere you'd add an operation. This might be wrapped in a method call of its own, but lazy initialization here probably isn't really necessary as queues are pretty light-weight.
You should create an NSAutoreleasePool at the start of the method that the NSOperation will invoke (in this case, firstRun), and drain it at the end of the method.

Generic approach to NSManagedObjectContext in multi-threaded application

I've read a number of posts here about NSManagedObjectContext and multi-threaded applications. I've also gone over the CoreDataBooks example to understand how separate threads require their own NSManagedObjectContext, and how a save operation gets merged with the main NSManagedObjectContext. I found the example to be good, but also too application specific. I'm trying to generalize this, and wonder if my approach is sound.
My approach is to have a generic function for fetching the NSManagedObjectContext for the current thread. The function returns the NSManagedObjectContext for the main thread, but will create a new one (or fetch it from a cache) if called from within a different thread. That goes as follows:
+(NSManagedObjectContext *)managedObjectContext {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:#"%p", thread];
// delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
if ( [managedObjectContexts objectForKey:threadKey] == nil ) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] autorelease];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
// cache the context for this thread
[managedObjectContexts setObject:threadContext forKey:threadKey];
}
return [managedObjectContexts objectForKey:threadKey];
}
Save operations are simple if called from the main thread. Save operations called from other threads require merging within the main thread. For that I have a generic commit function:
+(void)commit {
// get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
NSError *error;
if (![moc save:&error]) {
// fail
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
In the contextDidSave: function we perform the merge, if called by the notification in commit.
+(void)contextDidSave:(NSNotification*)saveNotification {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:YES];
}
Finally, we clean-up the cache of NSManagedObjectContext with this:
+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(threadExit)
name:NSThreadWillExitNotification
object:nil];
}
+(void)threadExit {
MyAppDelegate *delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:#"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
[managedObjectContexts removeObjectForKey:threadKey];
}
This compiles and seems to work, but I know threading problems can be tricky due to race conditions. Does anybody see a problem with this approach?
Also, I'm using this from within the context of an asynchronous request (using ASIHTTPRequest), which fetches some data from a server and updates and inserts the store on the iPhone. It seems NSThreadWillExitNotification doesn't fire after the request completes, and the same thread is then used for subsequent requests. This means the same NSManagedObjectContext is used for separate requests on the same thread. Is this a problem?
A year after posting this question I finally built a framework to generalize and simplify my working with Core Data. It goes beyond the original question, and adds a number of features to make Core Data interactions much easier. Details here: https://github.com/chriscdn/RHManagedObject
I found a solution after finally understanding the problem better. My solution doesn't directly address the question above, but does address the problem of why I had to deal with threads in the first place.
My application uses the ASIHTTPRequest library for asynchronous requests. I fetch some data from the server, and use the delegate requestFinished function to add/modify/delete my core-data objects. The requestFinished function was running in a different thread, and I assumed this was a natural side-effect of asynchronous requests.
After digging deeper I found that ASIHTTPRequest deliberately runs the request in a separate thread, but can be overridden in my subclass of ASIHTTPRequest:
+(NSThread *)threadForRequest:(ASIHTTPRequest *)request {
return [NSThread mainThread];
}
This small change puts requestFinished in the main thread, which has eliminated my need to care about threads in my application.