NSThread question - DO to make it sing
NSThread question - DO to make it sing
- Subject: NSThread question - DO to make it sing
- From: Candide Kemmler <email@hidden>
- Date: Thu, 12 Jul 2001 11:35:51 +0200
Dibut du message riexpidii :
>
De : Candide Kemmler <email@hidden>
>
Date : Jeu 12 jul 2001 11:20:31 Europe/Paris
>
@ : "Dennis C. De Mars" <email@hidden>
>
Cc : email@hidden
>
Objet : Rip : NSThread question - DO to make it sing
>
>
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