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: Thu, 12 Jul 2001 11:20:31 +0200
Le jeudi 12 juillet 2001, ` 09:58, Dennis C. De Mars a icrit :
>
on 7/11/01 10:39 AM, Candide Kemmler at email@hidden wrote:
>
>
>
>
> Le mercredi 11 juillet 2001, ` 06:57, Dennis C. De Mars a icrit :
>
>
>
>> on 7/11/01 8:47 AM, Candide Kemmler at email@hidden wrote:
>
>>
>
>>> 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 ?
>
>>
>
>> If your "renderMyImage" routine used any Swing classes to do its
>
>> rendering,
>
>> you'd have a problem with this code too, because Swing is not
>
>> thread-safe
>
>> either; you are supposed to do all drawing in the main thread there
>
>> too.
>
>>
>
>> I think this is true of pretty much any GUI system of any complexity
>
>> ever
>
>> devised -- you simply have to defer drawing to the main thread.
>
>>
>
>> - Dennis D.
>
>
>
> consider the following renderMyImage implementation (not tested):
>
>
>
> java.awt.Image renderMyImage () {
>
> java.awt.image.BufferedImage image = new
>
> java.awt.image.BufferedImage ( 450, 345, BufferedImage.TYPE_INT_ARGB );
>
> Graphics2D g2d = image.createGraphics ();
>
> g2d.setColor ( Color.red );
>
> g2d.fillRect ( 0, 0, 450, 345 );
>
> return image;
>
> }
>
>
>
> There's no Swing code involved, and now I have an image I can send to a
>
> Swing component to draw it with something like
>
>
>
> g.drawImage ( image, 0, 0, this );
>
>
>
> Simple enough, huh ? And that's exactly what I'd like to do in
>
> OC/Cocoa.
>
>
Well, if you are talking about rendering an image offscreen in a
>
secondary
>
thread, but only displaying it onscreen in the main thread, I think you
>
should be able to do that safely in Cocoa...if you look at:
>
>
http://developer.apple.com/techpubs/macosx/ReleaseNotes/ThreadSupport.html
>
>
...they have a discussion of modifying NSViews in threads. Although I
>
could
>
have used a more thorough discussion than Apple provides in this release
>
note, they seem to imply that the only thing to really avoid is doing
>
actual
>
on-screen updates anywhere but in the main thread, which is
>
understandable
>
since otherwise the main thread and another thread could be trying to
>
modify
>
the same on-screen pixels at the same time. But, if you have a thread
>
drawing to an off-screen image that is owned by that thread, that should
>
work. At least that's the impression I get, I haven't done too much of
>
this
>
myself.
>
>
The original code you posted is doing its work offscreen, so I think it
>
should be all right. Some of the errors you were getting might be due
>
to the
>
fact that the line:
>
>
[ NSThread detachNewThreadSelector:@selector(renderImage)
>
toTarget:mapRenderer withObject:data ];
>
>
should have been
>
>
[ NSThread detachNewThreadSelector:@selector(renderImage:)
>
toTarget:mapRenderer withObject:data ];
>
>
The colon is part of the selector name, so I think that's why you got
>
several messages about an unrecognized selector.
Right, I didn't get that. It works now... most of it: it seems that the
image is rightly created and rendered, but nothing appears on-screen.
This is the whole chain of calls:
in MapController, when the button's triggered:
[ NSThread detachNewThreadSelector:@selector(renderImage:)
toTarget:mapRenderer withObject:data ];
in MapRenderer, this is the renderImage method:
- (oneway void) renderImage: (bycopy NSData *) data
{
id mainProxy;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSSize aSize;
NSImage *cachedImage;
NSLog ( @"in renderImage code" );
aSize = NSMakeSize ( 550, 445 );
cachedImage = [[NSImage alloc] initWithSize:aSize];
[cachedImage setBackgroundColor:[[NSColor redColor]];
[cachedImage lockFocus];
[[NSColor blackColor] set];
[NSBezierPath fillrect:NSMakeRect(0,0,aSize.width,aSize.height)];
[cachedImage unlockFocus];
[mainProxy setImage:cachedImage];
[pool release];
}
back in MapController, the Image is set (MapController is the
"mainThread" DO):
- (void) setImage:(NSImage *) anImage
{
NSLog ( @"setImage is invoked, its size is %f, %f", [anImage
size].width, [anImage size].height );
[mapView setImage:anImage ];
}
in MapView, setImage is defined like so:
- (void) setImage:(NSImage *) anImage
{
NSLog (@"set cachedImage in MapView" );
cachedImage = image;
NSLog ( @"setImage is invoked, its size is %f, %f", [cachedImage
size].width, [cachedImage size].height );
[self display];
}
Now, drawRect:
- (void) drawRect: (NSRect) frame
{
float cx = [self bounds].size.width/2;
float cy = [self bounds].size.height/2;
NSLog ( @"drawRect is invoked, cachedImage's size is %f, %f",
[cachedImage size].width, [cachedImage size].height );
cx -= [cachedImage size].width/2;
cy -= [cachedImage size].height/2;
[cachedImage compositeToPoint:NSMakePoint(cx,cy)
operation:NSCompositeSourceOver];
}
The size of the image is rightly logged every time, yet nothing appears
on-screen (neither a black nor a red rectangle). Everything seems
all-right to me though. Any Idea ? Oh, And my app just crashes with a
signal 10 (SIGBUS) when I try to resize its window...
Thanks for your help.
Candide
>
- Dennis D.
>
_______________________________________________
>
cocoa-dev mailing list
>
email@hidden
>
http://www.lists.apple.com/mailman/listinfo/cocoa-dev