Recently i decided to use the SKPSMTPMessage Class for transferring images to a server.
I subclassed the NSOperation Class, implemented the funktionality and added it to a NSOperationQueue, because i don't want my app to be blocked and the user can't do anything while its uploading the image. This usually occur, when i use the GSM network and it lasts a long time, until it the image is being sent. (By the way, i don't want to do any compressions on the image)
- (void)main {
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail = [[_from copy] autorelease];
testMsg.toEmail = [[_to copy] autorelease];
testMsg.relayHost = [[_relayHost copy] autorelease];
testMsg.subject = [[_subject copy] autorelease];
testMsg.delegate = self.delegate;
testMsg.parts = [[_mailParts copy] autorelease];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];}
To be clear, i release the obect in the delegate i assign to the SKPSMTMessage instance. So it's not causing any leaks.
The Problem is, that i have to use performSelectorOnMainThread: because otherwise its not working. It stops right after
C: Attempting to connect to server at: mail.example.com:25
I've found this post here, that describes pretty much the same problem i'm currently facing, but i can't figure out, how its going to work.
The solution they described was, that they used to run the program in another thread.
[NSThread detachNewThreadSelector:#selector(launchJobWithJob:) toTarget:self withObject:jobDescription];
[[NSRunLoop currentRunLoop] run];
But when i do this without using the NSOperation subclass, it causes me this error:
_NSAutoreleaseNoPool(): Object 0x18a140 of class NSCFString autoreleased with no pool in place - just leaking
but still not working. It again only prints this "C: Attempting to connect to server at: mail.example.com:25".
Can anyone please help?
EDIT
In the Subclass NSOperation I now use instead of
[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];
that code.
[testMsg send];
[[NSRunLoop currentRunLoop] run];
It helps me getting across the freezing problem, but the reliability of the message being sent is not given.
It works now! I use following code in the NSOperation Subclass (btw: My subclasses name is SMTPSendOperation):
- (void)main
{
SKPSMTPMessage *testMsg = [[SKPSMTPMessage alloc] init];
testMsg.fromEmail = [[_from copy] autorelease];
testMsg.toEmail = [[_to copy] autorelease];
testMsg.relayHost = [[_relayHost copy] autorelease];
testMsg.subject = [[_subject copy] autorelease];
testMsg.delegate = self.delegate;
testMsg.parts = [[_mailParts copy] autorelease];
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//[testMsg performSelectorOnMainThread:#selector(send) withObject:nil waitUntilDone:NO];
//[NSThread detachNewThreadSelector:#selector(send) toTarget:<#(id)#> withObject:<#(id)#>]
[testMsg send];
[[NSRunLoop currentRunLoop] run];
}
Anyhow, i'll have to tell the user to be patient until the mail is being sent successfully or failed. If anyone has any idea, how i can use the thread to be run more efficient, i'll really appreciate that!
Related
After I update my Core Data store - by deleting then adding the data - in a different thread, I'm required to change the screen and then go back for the data to update it. Is there a way to do update Core Data without having to change the screen in the app?
code to reset database:
- (void) resetDatabase {
count++;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
ConDAO *con = [[ConDAO alloc] init];
DatabaseManager *manager = [DatabaseManager sharedManager];
NSError * error;
NSURL * storeURL = [[[manager managedObjectContext] persistentStoreCoordinator] URLForPersistentStore:[[[[manager managedObjectContext] persistentStoreCoordinator] persistentStores] lastObject]];
[[manager managedObjectContext] reset];//to drop pending changes
if ([[[manager managedObjectContext] persistentStoreCoordinator] removePersistentStore:[[[[manager managedObjectContext] persistentStoreCoordinator] persistentStores] lastObject] error:&error])
{
// remove the file containing the data
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
//recreate the store like in the appDelegate method
[[[manager managedObjectContext] persistentStoreCoordinator] addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error];//recreates the persistent store
}
NSLog(#"*****************************");
NSLog(#"updating");
NSLog(#"count: %d", count);
NSLog(#"*****************************");
[self populateDatabase:0 con:con];
NSTimer *timer = [NSTimer timerWithTimeInterval:60.0
target:self
selector:#selector(resetDatabase)
userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
dispatch_async(dispatch_get_main_queue(), ^(void){
});
});
}
Code that runs when ui is changed:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// Setup KVO for verifyingcard
[self addObserver:self forKeyPath:#"verifyingCard" options:NSKeyValueObservingOptionNew context:nil];
if([BluetoothTech isEqualToString:#"BLE"]){
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:#{CBCentralManagerOptionShowPowerAlertKey: #YES}];
}
else if([BluetoothTech isEqualToString:#"HID"]){
[self.bluetoothScanTextView becomeFirstResponder];
}
[self loadStudents];
}
I think it has to do with the loadStudents() function, but when I use NSNotificationCenter to run it from the other class, it still doesnt work.
LoadStudent code:
- (void)loadStudents{
NSError *error = nil;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"Caf_student_cards"];
NSArray *arr = [[self.manager managedObjectContext] executeFetchRequest:fetchRequest error:&error];
for(int i = 0; i < [arr count]; i++){
if([[[arr objectAtIndex:i] valueForKey:#"user_id"] isEqualToString:#"201509061"]){
NSLog(#"%#",[arr objectAtIndex:i]);
}
}
if(!error){
self.caf_student_cards = [NSMutableArray arrayWithArray:arr];
self.keys = [[[arr.firstObject entity] attributesByName] allKeys];
}
else{
NSLog(#"%s %s %s","\n\n\n",[[error localizedDescription] UTF8String],"\n\n\n");
dispatch_async(dispatch_get_main_queue(), ^{
// Show alert to tell user to reload this page
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:#"Error: %#", [error localizedDescription]] message:#"Check connection and relog back into cafeteria." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
});
}
}
There are multiple problems with this code.
The one most directly relevant to your question is that you're updating your persistent store, but not updating your UI. It doesn't happen automatically. If you have new data, you need to tell your UI to update. How to do that depends on what kind of UI you have. If it's a table view, it might be as simple as telling the table view to reload its data. If you have an array that you use to hold the UI's data, you would need to update that too (it looks like this might be what caf_student_cards is in your code but it's impossible to be sure.
Other problems-- and these are major problems that you need to fix immediately:
You're doing Core Data multithreading wrong. Using dispatch_async is not effective here. You need to use performBlock or performBlockAndWait on your managed object context, or else performBackgroundTask on your persistent container.
You're removing the main persistent store file but not removing the journal files. This is pretty much guaranteed to either prevent old data from being deleted or else simply cause data corruption. What you're doing is not a useful technique. Removing the persistent store files is rarely a good idea. If you want to get rid of existing data, delete it from Core Data, maybe by telling your context to delete the objects or else by using NSBatchDeleteRequest.
There may be others. This code is a mess. You would be doing yourself a huge favor if you spent a little time looking over Apple's Core Data Programming Guide.
Also you keep asking nearly the same question repeatedly. You've had some good advice, but you don't seem to be taking any of it. If you want more information, go and read over other answers people have already given when you've posted this question before.
I have NSXMLParser problem, and i tried iOS8 NSXMLParser crash this topic, but i really did not get the solution.
I am creating another NXSMLParser delegate and setting its delegate in another class.
Could you please tell me what to do exactly, step by step? I am so confused.
Here is my code;
These lines of codes are inside the STXMLParser
STXMLParser2 *stXMLParser2 = [[STXMLParser2 alloc]init];
stXMLParser2.xmlParser = [[NSXMLParser alloc] initWithData:responseLoader.xmlData];
[stXMLParser2.xmlParser setDelegate:self];
[stXMLParser2.xmlParser setShouldResolveExternalEntities:YES];
[stXMLParser2.xmlParser parse];
You can try this code:
dispatch_queue_t reentrantAvoidanceQueue = dispatch_queue_create("reentrantAvoidanceQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(reentrantAvoidanceQueue, ^{
STXMLParser2 *stXMLParser2 = [[STXMLParser2 alloc]init];
stXMLParser2.xmlParser = [[NSXMLParser alloc] initWithData:responseLoader.xmlData];
[stXMLParser2.xmlParser setDelegate:self];
[stXMLParser2.xmlParser setShouldResolveExternalEntities:YES];
[stXMLParser2.xmlParser parse];
});
dispatch_sync(reentrantAvoidanceQueue, ^{ });
I was getting the same error and it turned out that the problem was due to calling a UI update in the func parserDidEndDocument(parser: NSXMLParser) which does not run on the main thread. After forcing the UI update in that function to run on the main queue, the problem was resolved.
I encountered the same problem recently but it turned out that I had an exception in one of my delegates (KVO problem) and once I fixed that the reentracy error went away. So it might be worth to look for something else if you don't have an obvious multithreading or multiinstance problem.
I m very new to iOS, as stated in the question above; im trying to do these 3 simple step.
Show Alert view
Do parsing stuff
Dismiss Alert
I was looking for something like we have in android i.e Pre Execute, doInBackground and Post Execute().
This is what i have tried.
parserAlert = [[UIAlertView alloc] initWithTitle:#"Loading" message:#"Please Wait" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[parserAlert show];
dispatch_queue_t queue = dispatch_queue_create("com.abc.testing", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue,^{
DBHandler *myDB= [[DBHandler alloc] init];
[myDB fetchResults];
dispatch_async(dispatch_get_main_queue(),^{
[parserAlert dismissWithClickedButtonIndex:0 animated:YES];
});
});
Below is the fetchResult method.
- (void) fetchResults
{
IParser *i = [[IParser alloc] init];
[i startParse];
AGParser *ag = [[AGParser alloc] init];
[ag startParse];
GParser *g = [[GParser alloc] init];
[g startParse];
HParser *h = [[HParser alloc] init];
[h startParse];
SParser *s = [[SParser alloc] init];
[s startParse];
}
This is startParse.
NSString *url = #"http://abcd.com/Service_URL/Service.asmx/GetNotes";
NSURL *nsUrl = [[NSURL alloc] initWithString:url];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:nsUrl];
NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:request delegate:self];
responseData = [[NSMutableData alloc] init];
[con start];
When i run the above code, Alerview show and dismiss within a second. Adding logs on methods i observed that fetchresults method return immediately and alert view gets dismiss. However fetchResults associated threads(Connection methods, Parser methods) keep executing but alerview is dismissed.
I need a guideline how to block the code until all associated methods are finished.
Thanks for your time.
I know this is not the answer you want, but don't use an alert view for this. A nice way to cover for time-consuming activity to is to put up a UIActivityIndicatorView, or a view that contains one, and set it spinning:
http://www.apeth.com/iOSBook/ch25.html#_uiactivityindicatorview
You can also prevent user interaction while the time-consuming activity is happening, with the shared application object's beginIgnoring... (and turn that off with endIgnoring... when you're done). Obviously you can't do that, though, if the user is to be given a Cancel button. In that case, cover everything else with an invisible view (clear background color) whose userInteractionEnabled is YES, so that it eats any touches intended for anything other than the button.
Also, it is almost never the right answer to use dispatch_sync. Once you've frozen the interface in the way I've just described, you can just do your connections (asynchronous) and parsing (on a background thread) and then come back into the main thread to dismiss the activity indicator.
Finally, you're going to want to leave yourself a way out in case things go wrong. You could run an NSTimer, for example.
EDIT: And now for the actual answer to your actual question, i.e. why is my code not pausing even though I used dispatch_sync: it's because [[NSURLConnection alloc] initWithRequest:request delegate:self] returns immediately; the networking is in yet another background thread. So your startParse returns, your fetchResults returns, and meanwhile the networking continues and the NSURLConnection delegate methods are called some time later.
Here is the link what you are looking for MBProgressHUD
First alloc the MBProgressHUD instance of it in the viewDidLoad
MBProgressHUD *progressHUD = [[MBProgressHUD alloc] initWithView:self.view];
progress.delegate=self;
[progressHUD showWhileExecuting:#selector(performBackgroundTask) onTarget:self withObject:nil animated:YES]
and in the background method
-(void)performBackgroundTask
{
//Do some stuff
}
and soon as the task in the )performBackgroundTaskmethod is completed the Activity indicator shown in the MBProgressHUD will hidden and the delegate method called
-(void)hudWasHidden
{
//Do additional stuff after completion of background task
}
Hope it will help you.
This gives me memory leak, even thought I call create and release pool in the selector function.
[read_qr performSelectorInBackground:#selector(newThreadWrapper) withObject:nil];
class read_qr:
- (void) newThreadWrapper {
reading = YES;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self another_function:arg];
[pool release];
reading = NO;
}
There is no leak if I call the same function in the main thread without performSelectorInBackground.
Any tips are appreciated.
Thank you.
Apple's Grand Central Dispatch (GCD) is great, but only works on iOS 4.0 or greater. Apple's documentation says, "[A] serialized operation queue does not offer quite the same behavior as a serial dispatch queue in Grand Central Dispatch does" (because the queue is not FIFO, but order is determined by dependencies and priorities).
What is the right way to achieve the same effect as GCD's serial dispatch queues while supporting OS versions before GCD was released? Or put another way, what is the recommended way to handle simple background processing (doing web service requests, etc.) in iOS apps that want to support versions less than 4.0?
How about this PseudoSerialQueue? It is a minimal implementation like the Dispatch Serial Queue.
#import <Foundation/Foundation.h>
#interface PseudoTask : NSObject
{
id target_;
SEL selector_;
id queue_;
}
#property (nonatomic, readonly) id target;
- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
- (void)exec;
#end
#implementation PseudoTask
#synthesize target=target_;
- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
- (void)exec
{
[target_ performSelector:selector_];
}
- (void)dealloc
{
[target_ release];
[queue_ release];
}
#end
#interface PseudoSerialQueue : NSObject
{
NSCondition *condition_;
NSMutableArray *array_;
NSThread *thread_;
}
- (void)addTask:(id)target selector:(SEL)selector;
#end
#implementation PseudoSerialQueue
- (id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc] init];
condition_ = [[NSCondition alloc] init];
thread_ = [[NSThread alloc]
initWithTarget:self selector:#selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
- (void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask *task = [[PseudoTask alloc]
initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
- (void)quit
{
[self addTask:nil selector:nil];
}
- (void)execQueue
{
for (;;) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[condition_ lock];
while (array_.count == 0)
[condition_ wait];
PseudoTask *task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
- (void)dealloc
{
[array_ release];
[condition_ release];
}
#end
How to use:
PseudoSerialQueue *q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test2)];
[q quit];
Seems like people are going to a lot of effort to rewrite NSRunloop. Per the NSRunloop documentation:
Your application cannot either create
or explicitly manage NSRunLoop
objects. Each NSThread object,
including the application’s main
thread, has an NSRunLoop object
automatically created for it as
needed.
So surely the trivial answer would be, to create a usable queue:
- (void)startRunLoop:(id)someObject
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[NSRunLoop currentRunLoop] run];
[pool release];
}
...
NSThread *serialDispatchThread = [[NSThread alloc]
initWithTarget:self
selector:#selector(startRunLoop:)
object:nil];
[serialDispatchThread start];
To add a task to the queue:
[object
performSelector:#selector(whatever:)
onThread:serialDispatchThread
withObject:someArgument
waitUntilDone:NO];
Per the Threading Programming Guide section on Run Loops:
Cocoa defines a custom input source
that allows you to perform a selector
on any thread. ... perform selector requests are
serialized on the target thread,
alleviating many of the
synchronization problems that might
occur with multiple methods being run
on one thread.
So you've got an explicitly serial queue. Of course, mine isn't fantastically written because I've told the run loop to run forever, and you may prefer one you can terminate later, but those are easy modifications to make.
you can simulate it using NSOperationQueue, then just set the task count to one.
EDIT
-- oops, should have read more carefully. the fifo solution follows:
i can't think of a way that the majority of ios devs would use in your situation.
i'm not afraid of writing threaded programs, so here is one solution:
create a fifo worker queue that:
supports locking
holds one NSOperationQueue
holds an NSOperation subclass, designed to pull workers from the fifo queue in its implementation of main. only one may exist at a time.
holds an NSArray of workers to be run (defining a worker is up to you - is it an NSInvocation, class, operation, ...)
the NSOperation subclass pulls the workers from the fifo worker queue until the fifo worker queue is exhausted.
when the fifo work queue has workers and no active child operation, it creates a child operation, adds it to its operation queue.
there are a few pitfalls if you aren't comfortable writing threaded programs -- for this reason, this solution is not ideal for everybody, but this solution would not take very long to write if you are already comfortable using all the technologies required.
good luck
There are things NSOperationQueue documentation writer forgot to mention, making such implementation seem trivial when in fact it's not.
Setting the maximum concurrent operation count to 1 is guaranteed to be serial only
if NSOperations are added to the queue from same thread.
I'm using another option because it just works.
Add NSOperations from different threads but use NSCondition to manage queuing.
startOperations can (and should, you don't want to block main thread with locks) be called with performSelectorOnBackgroundThread...
startOperations method represents single job that consists of one or more NSOperations.
- (void)startOperations
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[AppDelegate condition] lock];
while (![[[AppDelegate queue] operations] count] <= 0)
{
[[AppDelegate condition] wait];
}
NSOperation *newOperation = [alloc, init]....;
[[AppDelegate queue] addOperation:newOperation];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!
NSOperation *newOperation1 = [alloc, init]....;
[[AppDelegate queue] addOperation:newOperation1];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!
NSOperation *newOperation2 = [alloc, init]....;
[[AppDelegate queue] addOperation:newOperation2];
[[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!
// Add whatever number operations you need for this single job
[[AppDelegate queue] signal];
[[AppDelegate queue] unlock];
[NotifyDelegate orWhatever]
[pool drain];
}
That's it!
If the processing is in the background anyway, do you really need it to be strictly in-order? If you do, you can achieve the same effect simply by setting up your dependencies so 1 depends on 0, 2 on 1, 3 on 2, etc. The operation queue is then forced to handle them in order. Set the maximum concurrent operation count to 1, and the queue is also guaranteed to be serial.