Re: NSThread question - DO to make it sing
Re: NSThread question - DO to make it sing
- Subject: Re: NSThread question - DO to make it sing
- From: Candide Kemmler <email@hidden>
- Date: Wed, 11 Jul 2001 17:47:49 +0200
Le mardi 10 juillet 2001, ` 06:54, Miguel Morales a icrit :
>
>
However, this would spawn a thread, but the main thread would wait for
>
the secondary thread to finish before continuing, and not give you any
>
of the functionality you want of the main thread plugging along. This
>
is where DO comes in. I will assume that there is a main object that
>
is running in a run loop, and that when the button gets pushed the
>
method -(void)button:(NSData *)data is called, and that there is
>
another method in the object that draws to the screen (also in the main
>
object) called -(oneway void)display:(bycopy NSData *)data.
Great ! I've read the chapter on DO in "Object-Oriented Programming and
the Objective-C Language" and I'm enthusiast about learning more on DO
in OC. For a java-fan like me, it's like doing RMI and multi-threading
at once ! However I find it a little odd to be forced to do rmi (or rpc,
or whatever-you-name-it) just to do multi-threading.
You said that the code snippet you give "would spawn a thread, but the
main thread would wait for the secondary thread to finish before
continuing" !!!!! That's the whole point of doing threads !
Consider the following bit of java code:
public void actionPerformed ( ActionEvent evt ) {
if ( evt.getSource () == myButton ) {
( new Thread () {
public void run () {
renderMyImage ();
}
} ).start ();
}
}
Isn't there a way as simple as this do achieve the same effect in
Objective-C/Cocoa ?
Anyway I've tried to go your way, but I wasn't lucky:
I wrote a MapRenderer class like this:
@implementation MapRenderer
- (oneway void) renderImage: (bycopy NSData *) data
{
id mainProxy;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init ];
NSSize aSize;
NSImage *cachedImage;
aSize = NSMakeSize (450, 345 );
mainProxy = [ NSConnection
rootProxyForConnectionWithRegisteredName:@"mainThread" host:@"" ];
cachedImage = [[NSImage alloc] initWithSize:NSMakeSize(aSize.width,
aSize.height)];
[ cachedImage lockFocus ];
[ [ NSColor blueColor ] set ];
[[ NSBezierPath bezierPathWithRect:NSMakeRect ( 0, 0, aSize.width,
aSize.height )] fill ];
[ cachedImage unlockFocus ];
[ mainProxy setImage: cachedImage ];
[ pool release ];
}
in my MapController class, I have a masterConnection defined in init
like you said and also a mapRenderer allocated and initialized.
Naturally I also have a setImage: (NSImage *) anImage method
When my button's triggered, I do:
[ NSThread detachNewThreadSelector:@selector(renderImage)
toTarget:mapRenderer withObject:data ];
The result is a catastrophy of errors and exceptions:
_NSAutoreleaseNoPool ():...
-[MapRenderer renderImage]: selector not recognized
just leaking, just leaking,...
selector not recognized..., InvalidArgumentException...
What's wrong ???
And a big thank you for your long explanations ! :-)
Regards,
Candide
PS: I don't get the following: "I will assume that there is a main
object that is running in a run loop". Isn't that my application ?
>
in the main object (main thread)
>
---------------
>
//In some initialization routine (-init?)
>
masterConnection = [NSConnection defaultConnection]; //Gets the default
>
connection for this thread
>
[masterConnection setRootObject:self];
>
[masterConnection registerName:@"mainThread"]; //registers itself
>
on the network so it can take input
>
.........
>
>
-(void)button:(NSData *)data
>
{
>
[NSThread detachNewThreadSelector:@selector(setData)
>
toTarget: myView withObject: data];
>
}
>
>
in the myView object (will be in a new thread most of the time)
>
---------------
>
-(oneway void) setData:(bycopy NSData *) data
>
{
>
id mainProxy; //A proxy object for the main object in the
>
main thread (how we call back when done)
>
>
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init];
>
>
mainProxy = [NSConnection
>
rootProxyForConnectionWithRegisteredName:@"mainThread" host:@""];
>
//picks up the network connection mainThread, and makes a
>
proxy object so we can call home
>
>
myData = data; //Already a copy from the bycopy keyword
>
[self doImage]; //do processing, but DO NOT write to the screen
>
>
[mainProxy display: myData];
>
//Calls back to the main thread, giving it the updated
>
information, and telling it to draw it to
>
//the screen (so only the main thread draws to the screen)
>
>
[pool release];
>
//Clean up afterwards, thread should automatically die
>
because we have no run loop for this thread
>
}
>
>
Note the oneway and bycopy keywords. oneway tells the DO system that
>
setData can be run independently of the main thread, otherwise the main
>
thread will wait for the sub thread to do it's processing (not what you
>
want). bycopy tells DO to send a copy of the data object, not a
>
proxy. This can greatly reduce the overhead of sending messages in
>
some situations.
>
>
Now the caveats: I just typed this in, and DO can be confusing, so
>
this is probably not bug free (some of the DO mavens out there can
>
point out my mistakes :-) The DO is hard to learn at first. Look at
>
Object-Orient Programming and the Objective-C Language, and some of the
>
DO examples. But once you get DO it is very cool. With a couple of
>
very minor changes the above code can be changed so that the secondary
>
thread persists and can process data whenever you want (just have the
>
thread vend a connection and send the thread into a run loop), or the
>
data can be processed by a process on another machine (some people
>
prefer to set up ports instead of registering a name when just doing
>
thread to thread communication, but I do distributed processing, and
>
like the elegance of the same interface whether the communication is
>
local or remote) So DO is definitely worth the time, and have fun.
>
>
Hope this helps,
>
>
-Miguel F. Morales
>
>
>
>
>
> Candide wrote:
>
>
>
>
>
> I still have an error (signal 10 (SIGBUS)). I'll explain exactly what I
>
> do:
>
>
>
> I have a -(void)doImage; method in a custom NSView object. This is the
>
> method I'm calling in the new Thread, and it goes like this:
>
>
>
> -(void)doImage
>
> {
>
> NSAutoreleasePool *pool = [[NSAutoreleasePool alloc ] init];
>
> [ self display ];
>
> }
>
>
>
> the method in which I call it goes like this:
>
>
>
> -(void) setData:(NSData *) data
>
> {
>
> myData = [[NSData alloc] initWithData:data];
>
> cachedImage = nil;
>
> [NSThread detachNewThreadSelector:@selector(doImage) toTarget:self
>
> withObject:nil];
>
> }
>
>
>
> Still obscure to me what causes the crash...
>
>
>
>> Le mardi 10 juillet 2001, ` 11:59, Stefan Jung a icrit :
>
>>
>
>> Am Dienstag, 10. Juli 2001 um 10:47 schrieb Candide Kemmler:
>
>>
>
>>> Hi,
>
>>>
>
>>> I have a button (several of them actually) which triggers a long
>
>>> rendering. My application not being multithreaded, the button stays
>
>>> blue for a long while, until the rendering's finished. I tried to do:
>
>>>
>
>>> [ NSThread detachNewThreadSelector:@selector(display) toTarget:self
>
>>> withObject:nil ];
>
>>>
>
>>> But then I get
>
>>>
>
>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d390
>
>>> of class NSBezierPath autoreleased with no pool in place - just
>
>>> leaking
>
>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d3f0
>
>>> of class NSBezierPath autoreleased with no pool in place - just
>
>>> leaking
>
>>> Jul 10 10:46:50 TT[979] *** _NSAutoreleaseNoPool(): Object 0x145d300
>
>>> of class NSCalibratedRGBColor autoreleased with no pool in place -
>
>>> just leaking
>
>>>
>
>>> What's wrong with my call ?
>
>> Your call is Ok, but you have to set up your own NSAutoreleasePool.
>
>> For
>
>> your primary thread this is done by your application object, for
>
>> Threads you have to do it on your own.
>
>>
>
>> NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
>
>> The *pool destroys itself at the next opportunity, but releases all
>
>> autoreleased objects before. so there is no [pool release].
>
>>
>
>> Stefan