How to kill NSThread while clicking on new tab iOS? - ios

I building an app has 4 tab (Tabbar Controller), and each tab I call a function (updateArray) after 2s. I want to when click on other tab, updateArray() function is kill. My problem is when on tab, updateArray() call after 2s, when I click on other tab, this function is still call.
This is updateArray()
-(void)updateArray{
while (loop)
{
[NSThread sleepForTimeInterval:2.0];
[FileCompletedArray removeAllObjects];
[temp removeAllObjects];
[UserNameArray removeAllObjects];
NSURL *url1 = [NSURL URLWithString:#"server"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL: url1] ;
NSMutableURLRequest *afRequest = [httpClient requestWithMethod:#"POST" path:nil parameters:params1] ;
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
NSString * parsexmlinput = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSLog(#"Response in Loop CompleteView: %#", parsexmlinput); //000400010001
// dispatch_async(dispatch_get_main_queue(), ^{
[self parseXMLFile:parsexmlinput];
NSLog(#"File Completed array: %#", FileCompletedArray);
NSLog(#"File Temp out array: %#", temp);
NSLog(#"File Completed count: %lu",(unsigned long)[ FileCompletedArray count]);
NSLog(#"File Temp out count: %lu", (unsigned long)[temp count]);
if([FileCompletedArray count] != [temp count])
{
temp = [FileCompletedArray mutableCopy];
NSLog(#"File Temp 1 array: %#", temp);
[_tableView reloadData];
NSLog(#"File Temp 2 array: %#", temp);
}
[alert dismissWithClickedButtonIndex:0 animated:YES];
//});
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error: %#", error);
}
];
[httpClient enqueueHTTPRequestOperation:operation];
}
}
And in viewwillappear()
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
loop = YES;
temp = [FileCompletedArray mutableCopy];
[self performSelectorInBackground:#selector(updateArray) withObject:nil ];
}
In my function, i used [NSThread sleepForTimeInterval:2.0];, I don't know how to kill it. Do you have suggestions ? Thanks in advance

You shouldn't really use sleepForTimeInterval, you should use performSelector:withObject:afterDelay: (and cancelPerformSelectorsWithTarget:) or dispatch_after.
As it is, you can add a BOOL attribute that is used to decide if the thread should continue after the sleep or whether it should exit (return).

To control any thread you have to use NSOperation Using this you can control any running thread.

Create a BOOL when you click on another tab set it to FALSE. Use this with dispatch after.
#property (nonatomic, assign) BOOL doUpdate;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(doUpdate){
//update work.
}
});
You should take look at using NSOperation and then you could call [NSOperation cancelAllOperations] when clicking on a another tab.
Good luck,
Booranger

Related

UIlabel text not getting update when download files in progress

I am trying to update the `UILabel` i.e downloaded data and remeaning data to be downloaded estimated time and total size of the downloading files via `NSnotificationCenter`, but not being updated `UILabel` text Please help me on this.
Also tried putting the `NSnotificationCenter` block in the main thread but no result found.
I have tried like this:
- (AFHTTPRequestOperation )downloadMediaOperation:(ILSCDowloadMedia )media success:(void (^)(ILSCDowloadMedia *media))success {
if (media.mediaUrl.length == 0) nil;
__block NSString *mediaKey = [[NSUserDefaults standardUserDefaults] objectForKey:media.mediaUrl];
NSURL *url = [NSURL URLWithString:media.mediaUrl];
if (mediaKey.length == 0) {
mediaKey = [NSString stringWithFormat:#"%#.%#", [ILSCUtility createUUID], [[[url path] lastPathComponent] pathExtension]];
}
NSFileManager *fileManager= [NSFileManager defaultManager];
NSString *mediaFilePath = NIPathForDocumentsResource(mediaKey);
media.mediaFilePath = mediaFilePath; if (![fileManager fileExistsAtPath:mediaFilePath]) {
__weak ILSCSyncManager *weakSelf = self;
NSURLRequest *request = [self.HTTPClient requestWithMethod:#"GET" path:[url path] parameters:nil];
AFHTTPRequestOperation *downLoadOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
downLoadOperation.downloadSpeedMeasure.active = YES; [downLoadOperation setShouldExecuteAsBackgroundTaskWithExpirationHandler:^{
// Clean up anything that needs to be handled if the request times out
// It may be useful to initially check whether the operation finished or was cancelled
}];
downLoadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:mediaFilePath append:NO];
[downLoadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[[NSUserDefaults standardUserDefaults] setObject:mediaKey forKey:media.mediaUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
if (success) {
success(media);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NIDERROR(#"y error %#", [error localizedDescription]);
__strong ILSCSyncManager *strongSelf = weakSelf;
strongSelf.numberOfDownloadErrors++;
}];
[downLoadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSLog(#"vvv Byted total expected to read %f",totalImagesBytesExpectedToRead);
totalImagesBytesRead += bytesRead;
humanReadableSpeed = downLoadOperation.downloadSpeedMeasure.humanReadableSpeed;
humanReadableRemaingTime = [downLoadOperation.downloadSpeedMeasure humanReadableRemainingTimeOfTotalSize:totalImagesBytesExpectedToRead numberOfCompletedBytes:totalImagesBytesRead];
NSLog(#"Speed Human %#",humanReadableSpeed);
NSLog(#"Time is human read %#",humanReadableRemaingTime);
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateProgressBar" object:[NSString stringWithFormat:#"%#-%f-%f-%#", humanReadableSpeed,totalImagesBytesRead,totalImagesBytesExpectedToRead,humanReadableRemaingTime]];
});
}];
return downLoadOperation;
} else {
if (success) {
success(media);
}
}
return nil;
}
Please help me on this.
This is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:#"UpdateProgressBar" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSString *str =[note object]; NSArray *arrayTotalOperationsIn = [str componentsSeparatedByString:#"-"];
NSLog(#"%#",arrayTotalOperationsIn); self.lblSpeedMeasure.text =[NSString stringWithFormat:#"Internet Speed - %#" ,[arrayTotalOperationsIn objectAtIndex:0]];
float bytesRead = [[arrayTotalOperationsIn objectAtIndex:1] floatValue];
float bytesExpectedToRead = [[arrayTotalOperationsIn objectAtIndex:2] floatValue];
NSString *timeExpectedToRead = [arrayTotalOperationsIn objectAtIndex:3];
self.progressCountTextLabel.text=[NSString stringWithFormat:#"%.2f MB/%.2f MB - %# Left",bytesRead/1000000,bytesExpectedToRead/1000000,timeExpectedToRead];
}];
The above is the listener of the NSnotification please check and please let me know.
I add this class as Loader while once down load starts.
I have gone through some of the sites as i got some information NSOperation queue is runs in the background thread . i am not sure on this please help me .
Try calling the setNeedsDisplay method on your UILabel after setting the text
[self.progressCountTextLabel setNeedsDisplay];

SVProgressHUD dismiss not working in custom class

Firstly, I've added some functions which need to call from different ViewControllers in one class file. For those function, I've add SVProgressHUD as preprocess and dismiss after all process is finished. SVProgressHUD is displaying correctly before process is started. But after process, SVProgressHUD is never dismissed at all. Please let me know how to solve that issue.
I've added [SVProgressHUD dismiss]; in all process is finished. But never dismissed.
common.m
- (NSDictionary *) loadBlahClass:(NSString *)paramUserId {
[SVProgressHUD showWithStatus:#"Loading Cards..."];
__block NSDictionary *jsonResponse;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.completionQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
manager.requestSerializer = [AFJSONRequestSerializer serializer];
[manager.responseSerializer.acceptableContentTypes setByAddingObject:#"application/json"];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSString *urlStr = [NSString stringWithFormat:#"MY_URL"];
[manager GET:urlStr parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSString *jsonString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
[SVProgressHUD dismiss];
dispatch_semaphore_signal(semaphore);
}
failure:
^(AFHTTPRequestOperation *operation, NSError *error) {
[SVProgressHUD dismiss];
[self showAlert:APP_NAME alertMessage:[error localizedDescription]];
dispatch_semaphore_signal(semaphore);
}];
[SVProgressHUD dismiss];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return jsonResponse;
}
testViewController.m
_allTableData = [[NSMutableArray alloc]init];
NSDictionary *jsonResponse = [commClass loadBlahClass:strUserId];
NSNumber *status = [jsonResponse valueForKey:#"Success"];
NSString *message = [jsonResponse valueForKey:#"Message"];
NSArray *dataArray = [jsonResponse valueForKey:#"lstCC"];
if([status intValue] == 1) {
for(int i=0; i<[dataArray count]; i++) {
//working .......
}
[_ccTable reloadData];
[SVProgressHUD dismiss];
} else {
[commClass showAlert:APP_NAME alertMessage:message];
[SVProgressHUD dismiss];
}
I've found answer. Something like that.
[SVProgressHUD show];
__block BOOL result;
dispatch_async(queue, ^{
result = [self autanticate];
NSLog(#"autantication result = %d", result);
result = [self getCSRFToken];
NSLog(#"Login success result = %d", result);
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
})
});

Progress View and background loading

my code is loading 7 pics from url and adding their data to an array. in the end of the process I do get an 8 objects array, but I'm trying to show a progress bar until the process of loading all the photos finished.
I do not have an idea how to do that...
here is the code
-(void)SetUpDrinks
{
loadingView.hidden=NO;
[loadingView_activity startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible=YES;
imgsDATA = [[NSMutableArray alloc] init];
for (int i=0; i<8; i++) {
imageDownloadNum++;
absPath = [NSString stringWithFormat:#"http://domain/app/menu/drinks/%i.png",imageDownloadNum];
trimmedAbsPath = [absPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSURL *imgURL = [NSURL URLWithString:trimmedAbsPath];
NSLog(#"%#",imgURL);
imgDATA = [[NSData alloc] initWithContentsOfURL:imgURL];
[imgsDATA addObject:imgDATA];
}
dispatch_async(dispatch_get_main_queue(), ^{
loadingView.hidden=YES;
[loadingView_activity stopAnimating];
[UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
[self RefreshImg];
});
});
}
You could make an NSOperation subclass to download your image and then use a dispatch_group. Dispatch groups are a way to block a thread until one or more tasks finish executing - in this scenario we are waiting for all of your downloads to finish.
You can now use the progressBlock to update the UI and let the user know how many of the images have finished downloading.
If you want progress for an individual download then take a look at the NSURLConnection reference. In particular - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- (void)enqueueGroupOfOperations:(NSArray *)operations
progress:(void (^)(NSUInteger completedCount, NSUInteger totalOperations))progressBlock
completion:(void (^)(NSArray *operations))completionBlock;
{
NSParameterAssert(operations);
NSParameterAssert(progress);
NSParameterAssert(completion);
__block dispatch_group_t group = dispatch_group_create();
NSBlockOperation *dependentOperation = [NSBlockOperation blockOperationWithBlock:^{
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
completion(operations);
});
dispatch_release(group);
}];
for (NSOperation *operation in operations) {
operation.completionBlock = ^{
dispatch_group_async(group, dispatch_get_main_queue(), ^{
NSUInteger count = [[operations indexesOfObjectsPassingTest:^BOOL(NSOperation *operation, NSUInteger idx, BOOL *stop) {
return [operation isFinished];
}] count];
progress(count, [operations count]);
dispatch_group_leave(group);
});
};
dispatch_group_enter(group);
[dependentOperation addDependency:operation];
}
[self.operationQueue addOperations:operations waitUntilFinished:NO];
[self.operationQueue addOperation:dependentOperation];
}
If this is too much for you then you can go over to AFNetworking where this is all done for you, https://github.com/AFNetworking/AFNetworking. But its always nice to know how some of this stuff works.

AFNetworking Memory

Using AFNetworking to download files from a server. Here's the code:
self.networkQueue = [[[NSOperationQueue alloc] init] autorelease];
[networkQueue setMaxConcurrentOperationCount:3];
for(NSDictionary* fileDictionary in self.syncArray) {
#autoreleasepool {
if([[fileDictionary allKeys] containsObject:#"downloadZipURL"]) {
NSString* downloadPath = [fileDictionary objectForKey:#"downloadZipURL"];
downloadPath = [downloadPath stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSURLRequest *requestURL = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadPath]];
NSString* localDestPath = [NSString stringWithFormat:#"%#/%#", [FileUtil userDocumentsDirectory], [downloadPath lastPathComponent]];
NSString* localTempPath = [NSString stringWithFormat:#"%#.tmp", localDestPath];
[(NSMutableDictionary*)fileDictionary setObject:localDestPath forKey:#"downloadDestination"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:localDestPath append:NO];
operation.userInfo = fileDictionary;
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
// [operation setDownloadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// NSLog(#"Sent %lld of %lld bytes, %#", totalBytesWritten, totalBytesExpectedToWrite, localDestPath);
// float progress = (float)totalBytesWritten/(float)totalBytesExpectedToWrite;
// [(NSMutableDictionary*)operation.userInfo setObject:[NSString stringWithFormat:#"Downloading %.0f%%", progress*100] forKey:#"downloadStatus"];
// [(NSMutableDictionary*)operation.userInfo setObject:[NSNumber numberWithFloat:progress] forKey:#"downloadProgress"];
// [syncViewController onPermitUpdated];
// }];
[networkQueue addOperation:operation];
}
}
}
My problem is that once this code is run, memory slowly gets eaten up and never given back. Now, these can be large files, which is why I used the outputStream.
Any suggestions would be appreciated.
Off the top of my head - I see that you're not using ARC.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:requestURL]
Are you releasing this operation somewhere?
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (networkQueue.operationCount == 0)
{
if(hasDownloadError || isCancellingSync) {
return ;
}
[self performSelectorInBackground:#selector(processAllFiles) withObject:nil];
}
Here, you're using the networkQueue in the completionBlock and the block retains the networkQueue, you then add the operation to the networkQueue, which retains the operation, which leads to neither of them deallocating. Try making a weak variable of the networkQueue and use that in order to break the cycle.
If these don't work - run instruments and make a note of what objects remain in memory and when their reference count is changed.

Perform upload after user selects Activity from UIActivityViewController - Possibly an issue with blocks

I want to perform an upload task once a user has selected a share activity from UIActivityViewController, but before the share sheet is shown.
Specifically, I need the url of the uploaded image to use in the Activity.
I already have subclassed UIActivityItemProvider and figure I can do my uploading in the itemForActivityType method, however the uploading code is block based and I can't figure out how to make it wait for the block to finish. Is this even possible?
It might be a simple coding error, it's been a long day.
I dont want to upload the image when the user presses the share button, as they might cancel the Activity View which means the uploaded image is sitting there not being used.
This is the code I currently have, but it returns nil before the image has uploaded and within the block it doesn't let me return nil for the errors:
- (id) activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType
{
[self getShortUrlForUploadedImageWithCompletionHandler:^(NSString *shortUrl, NSError *error) {
if (!error) {
if ( [activityType isEqualToString:UIActivityTypeMail] ) {
NSString *shareString = #"Email content here using shortUrl";
return shareString;
} else {
return #"";
}
} else {
return #"";
}
}];
return nil;
}
-(void)getShortUrlForUploadedImageWithCompletionHandler:(NSString* (^)(NSString *shortUrl, NSError *error))completionHandler
{
NSData *imageToUpload = UIImageJPEGRepresentation(_image, 75);
AFHTTPClient *client= [AFHTTPClient clientWithBaseURL:[NSURL URLWithString:kShareURL]];
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
#"image", #"action",
#"simple", #"format",
nil];
NSMutableURLRequest *request = [client multipartFormRequestWithMethod:#"POST" path:nil parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData) {
[formData appendPartWithFileData: imageToUpload name:#"image" fileName:#"temp.png" mimeType:#"image/jpeg"];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *response = [operation responseString];
NSLog(#"response: %#",response);
completionHandler(response, nil);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if([operation.response statusCode] == 403){
NSLog(#"Upload Failed");
return;
}
NSLog(#"error: %#", [operation error]);
completionHandler(nil, error);
}];
[operation start];
}
-------- EDIT
I really could do with some help on this. My current work around is to upload the image when the user click my share button, before the Activity selection. So they could cancel the share and i'm left with a redundant uploaded image, or they could select Twitter which doesn't need the uploaded image.
I need to only upload the image if Email has been selected and I think the only place I can do this is in the Acticity Provider subclass.
Instead of implementing - (id)activityViewController:(UIActivityViewController *)activityViewController itemForActivityType:(NSString *)activityType, try overriding the UIActivityItemProvider's - (id)item. This method will be called from the NSOperation's main method which is on a background thread.
As for waiting until the networking completion block triggers, I'd recommend you look into using a dispatch_semaphore. Here is an example:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"doing some work");
sleep(5);
NSLog(#"done with work");
dispatch_semaphore_signal(semaphore);
});
double delayInSeconds = 60.0;
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
NSLog(#"waiting for background thread to finish");
dispatch_semaphore_wait(semaphore, waitTime);
NSLog(#"background thread finished, or took too long");
Make sure to only use this on a background thread though, otherwise you will block the main thread.

Resources