Calling NSPathControl object in thread crashes - objective-c

For some reason calling an NSPathControl object in a thread is causing crashes.
- (IBAction) action5:(id)sender {
[outlet_NSPathControl1 setURL: [NSURL fileURLWithPath: #"/Users/admin/"]]; // Works fine here
[self performSelectorInBackground:#selector(background1) withObject:self]; // Jump to the thread
}
-(void) background1 {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[outlet_NSButton1 setTitle: [NSString stringWithFormat: #"%d", index]];
[outlet_NSPathControl1 setURL:[NSURL fileURLWithPath: #"/Users/admin/"]]; // Crashes here
[pool drain];
}

"Crash" isn't really descriptive enough to offer any specific help, but if a class isn't listed as being thread safe, then it probably isn't.
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
UI elements should also generally only be updated from the main thread.

Related

Xcode Objective C - Help with NSAutoreleaseNoPool error using NSThread

Hey experts, I'm having a little trouble with NSThread. Xcode keeps on giving me "* __NSAutoreleaseNoPool(): Object 0x5694dc0 of class NSCFString autoreleased with no pool in place - just leaking" errors.
I'm correctly declaring the pool with the line
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
then at the end of my loop I use:
[pool release];
Is it because I'm using a delegate method as the performSelectorInBackground?
Thanks stackoverflow.
- (void)preFetch { //process filenames to be downloaded and assign types to each one
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *regions = [NSArray arrayWithObjects: #"dr_national", #"ds_ir", #"conus_FL360", #"FL360_conus", #"dr_nw", #"dr_nc", #"dr_ne", #"dr_sw", #"dr_sc", #"dr_se", #"ds_ir_nw", #"ds_ir_nc", #"ds_ir_ne", #"ds_ir_sw", #"ds_ir_sc", #"ds_ir_se", nil];
NSError* error;
for (NSString *regionDir in regions) {
NSLog(#"region now: %#", regionDir); foo = 0;
NSString *regUrl = [NSString stringWithFormat:#"http://someUrl/%#/index.lst", regionDir ];
NSString* text1 = [NSString stringWithContentsOfURL:[NSURL URLWithString:regUrl ] encoding:NSASCIIStringEncoding error:&error];
NSArray *listItems = [text1 componentsSeparatedByString:#"\n"];
for (int k=0; k<[listItems count]; k++) {
if ([[listItems objectAtIndex:k] length] != 0){
NSString *newpath = [NSString stringWithFormat:#"http://someUrl/%#", [listItems objectAtIndex:k]];
NSLog(#"newpath: %#",newpath);
[self performSelectorInBackground:#selector(moveProgressBar) withObject:nil];
[self fetchImages:newpath:type]; //pass multiple arguments to fetchImages, newpath and type
}
}
}
[pool release];
}
- (void)moveProgressBar{
[delegate increaseAmount];
}
You should just set up an autorelease pool in your method, since that's being called on a different thread.
- (void)moveProgressBar
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[delegate increaseAmount];
[pool drain];
}
Edit
Having said that, looking at the code itself, it seems that you might be trying to update the UI from a background thread? Any code that does that should be executed on the main thread.
If you have a long running process that you want to run which doesn't lock the UI, and keeps the user updated on progress, the typical pattern would be to do the processing itself on a background thread, and periodically update the UI using performSelectorOnMainThread:.

Error message that UIKit should not be called from a secondary thread

I have an app which uses a UISearchBar to dynamically search from an external API based on user input.
The app is searching the external API fine and displaying results correctly, but when I select any row from the search results, the screen freezes and I am getting this error;
Tried to obtain the web lock from a thread other than the main thread or the web thread
UIKit should not be called from a secondary thread
I have absolutely no idea how I can fix this.
Here is the code;
- (void) run: (id) param {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL: [self URL]];
[parser setDelegate: self];
[parser parse];
[parser release];
[delegate parseDidComplete];
[pool release];
}
- (void) parseXMLFile: (NSURL *) url
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self setURL: url];
NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:#selector(run
object: nil];
[myThread start];
[pool release];
}
"Tried to obtain the web lock from a
thread other than the main thread or
the web thread UIKit should not be
called from a secondary thread"
The fix is conceptually simple; don't update the UI from your thread.
Assuming the parseDidComplete is where the message is sourced, then something like this will "work":
[delegate performSelectorOnMainThread: #selector(parseDidComplete) withObject: nil waitUntilDone: YES];
"Work" because threading is hard and this answer completely ignores any synchronization issues you might have.
Note that you'd be better off using NSOperation and NSOperationQueue. They are well documented and there are a bunch of examples.
I would suspect the line:
[delegate parseDidComplete];
If the delegate class is interacting with UIKit components, then the background thread that is retrieved the XML contents is then calling the front-end objects which must all be in the main thread.
You may want to look at using an NSOperation and NSOperationQueue to do the asynchronous operations. I believe that provides a more threadsafe way to handle this type of use case.

Why am I getting *** _NSAutoreleaseNoPool(): Object 0x97480b0 of class NSCFDictionary autoreleased with no pool in place - just leaking

I have noted several other threads on this topic and have tried wrapping my threaded code with:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[pool release];
but the errors still come.
I am using a static method to instantiate a dictionary of words.
Here is some code:
-(id)init
[NSThread detachNewThreadSelector:#selector(loadDictionary:) toTarget:[IntroScreen class] withObject:nil];
[NSThread setThreadPriority:1.0];
return self;
}
+(void)loadDictionary:(id)param
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"click.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"pop.wav"];
[[SimpleAudioEngine sharedEngine] preloadEffect:#"dink.wav"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:#"musicloop.wav"];
[WordDictionary configDictionary];
[pool release];
}
+(void)configDictionary
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Serializer * mySerializer = [[Serializer alloc] init];
[WordDictionary setDictionary:[mySerializer readApplicationPlist:#"x"]];
NSString * string;
NSString *filePath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"x.txt"];
NSString *info = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSArray *arrayOfLines = [info componentsSeparatedByString:#"\r\n"];
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
int len = [arrayOfLines count];
for(int i = 0; i < len; i++)
{
string = [arrayOfLines objectAtIndex:i];
NSString * blankString = [NSString stringWithString:#""];
[[WordDictionary dictionary] setObject:blankString forKey:string];
double calc = ((double)i / (double)len) * 100.0;
[WordDictionary setProgress:(int)calc];
}
[mySerializer writeApplicationPlist:[WordDictionary dictionary] toFile:#"s"];
[WordDictionary setProgress:100];
[pool release];
}
Is there something I should know about using static class methods with new selector threads?
Thank you for your help
First, there are no static methods in Objective-C. There are class methods.
Secondly, your code shows both methods wrapped in autorelease pools. The warning must be coming from somewhere else.
Finally, your code leaks like a sieve. You aren't following the memory management rules. And there are some nonsense statements in there.
Specifically:
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]];
Unless +setDictionary: is breaking the memory management rules, the above leaks.
This statement [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; effectively does nothing unless you do something with the return value.
Also, mySerializer is leaking.
Try running the analyzer over your code and fixing the problem. You should also read this and this.
Ah the [NSMutableDictionary dictionaryWithContentsOfFile:filePath]; was part of an experiment I was attempting to make the dictionary access faster. I should have removed it from this example.
I have just read the memory management rules, and understand that
[WordDictionary setDictionary:[[NSMutableDictionary alloc] init]]; appears to be poorly planned instantiation because I have no way to release it from within configDictionary as the reference is lost. But actually I don't ever want to release it, it lives for the entire lifetime of my application. Probably bad practice just the same.
mySerializer should definitely be released at the bottom.
I was just wondering if class methods had any special rules regarding autorelease pools and memory.
I will look over the documents you sent me and try to figure out the Analyzer, thank you for your help.

Retaining an object created in an NSThread

I have the following method which is spawned by a call for a new thread (using NSThread):
- (void) updateFMLs {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *temp = [[NSArray alloc] initWithArray:someArrayFromAnotherProcess];
[self performSelectorOnMainThread:#selector(doneLoading:) withObject:temp waitUntilDone:NO];
[pool release];
}
My doneLoading: method looks like this:
- (void) doneLoading:(NSArray *)obj {
myArray = [[NSArray alloc] initWithArray:obj copyItems:NO];
}
The contents of myArray become invalid. How can I preserve the contents of myArray so I can use them later in my app?
P.S. myArray is defined in the class header file.
If your background thread does some work and needs to 'pass' an NSArray to your main thread, then all doneLoading needs to do is:
-(void)doneLoading:(NSArray *)obj
{
[myArray release]; // release the previous array and its objects
myArray = [obj retain];
// now use myArray, refresh tables, etc.
}
There's (likely) no need to make another copy of the array, and that might be the underlying issue. You should also call [temp release] after your performSelector call, since arguments to that are retained already.
If the contents of myArray are becoming valid somehow, then they are being doubly released somewhere. myArray will retain any objects that are added to it. You mentioned that myArray itself is becoming invalid, so try rewriting your background thread and your doneLoading method with this pattern.
Finally you should use [pool drain] in place of [pool release].
The code you posted looks fine, apart from the memory leak in updateFMLs. You're probably over-releasing the objects somewhere else. I'm guessing it would be wherever someArrayFromAnotherProcess is made.
An alternative to the options above would be to declare myArray as an atomic property in the header
#property (atomic,retain) NSArray *myArray;
Then in updateFMLs you should be able to simply call the setter from the secondary thread. Obviously this only works if you are willing to pay the performance penalty for an atomic property.
- (void) updateFMLs {
NSAutoreleasePool *pool - [[NSAutoreleasePool alloc] init];
NSArray *temp = [[NSArray alloc] initWithArray:someArrayFromAnotherProcess];
[self setMyArray:temp];
[temp release];
[pool drain];
}

Do multithreading and autorelease pools work together in Cocoa?

I would like to send an object back to the main thread from worker thread. However do auto-release pools work between threads? Is there anything wrong with the following code:
-(void)mainThreadReceiveResult:(id)response
{
[response retain];
/* Do some stuff with response */
[response release];
}
-(void)workerThreadDoWork
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
response * response = [[[response alloc] init] autorelease];
response->someData = [self getSomeData];
[delegate performSelectorOnMainThread:#selector(receiveResult:) withObject:response waitUntilDone:NO];
[pool release];
}
Seems to work fine. However is it possible that the worker thread could reach [pool release] before the main thread is able to retain it?
Your code shouldn't crash: performSelectorOnMainThread: retains its arguments until after the selector finishes, so your retain/release pair is superfluous.
See the documentation:
This method retains the receiver and the arg parameter until after the selector is performed.
Also: you should probably [pool drain] instead of [pool release].
This may answer your question.
Here's what he did to solve the problem. The explanation is given in that link.
- (void)runSomethingThatWillFail:(NSError **)error {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:#"/BOGUS" error:error];
[*error retain];
[pool release];
[*error autorelease];
}