discuss-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSImage / NSImageRep copy bug/problem


From: Ivan Vučica
Subject: Re: NSImage / NSImageRep copy bug/problem
Date: Sun, 30 Nov 2014 18:20:18 +0000

Without digging too deep in, and addressing other stuff than your direct issue, during code review these would be some of the red (or at the very least orange) flags for me in the code you pasted in email:

- I assume PRImage's bitRep field ivar is represented by the property bitmapRep; it is good practice to have the properties backed by either ivars of the same name, or prefixed by underscore
- You're having an instance of PRImage modify another instance of PRImage, which is fine, although preferably avoided -- and in this case avoidable. Should you really ->bitRep = nil? Doesn't [objCopy setBitmapRep:] do that already? 

Looking at PRImage.h, I'd change:
    @private NSBitmapImageRep *bitRep;
into
    @private
    NSBitmapImageRep *bitRep;
 to make it clearer that @private addresses not just that one ivar, but all that follow (until @public or so appear).

Next, your implementation of -setBitmapRep: looks quite odd to me in the first place. Do you really need to store a copy of the bitmap representation in your subclass of NSImage? Does it make sense to /just/ add a new representation (which may confuse NSImage)? Why not implement -bitmapRep: as
  return [[self representations] objectAtIndex:0];
and -setBitmapRep: as something that, if [self bitmapRep] != rep, clears away all representations and adds just one new one? That way you avoid a lot of unnecessary juggling in your own code -- including need to override copyWithZone:. Right now you have a very surprising behavior -- calling -setBitmapRep: actually doesn't release the old bitmap rep; it pretty much stays inside the representations array in NSImage.

If you do as above, suddenly it becomes feasible to implement this as nothing but a category over NSImage, right? Let's call this dynamic property -primaryRepresentation: and let's add all other methods into that category as well.

So I'd try fixing the issue by not having it at all.

And I may have spotted another bug just before sending the email, and it looks to me to be in -setBitmapRep:; at the very least I'd change these lines:

126 - (void)setBitmapRep:(NSBitmapImageRep *)rep
127 {
128  if (bitRep != rep)
129    {
130      [bitRep release];
131      bitRep = rep;
132      [bitRep retain];
133      [self addRepresentation:bitRep];
134    }
135 }

This should read:

[rep retain];
[bitRep release];
bitRep = rep;

In fact, even better would be to also move call to -addRepresentation: to the first place::

[self addRepresentation:rep];
[rep retain];
[bitRep release];
bitRep = rep;
 
Note that doing /just/ that would probably mask the refcounting bug (that is, it wouldn't manifest), as NSImage's code (which probably uses NSMutableArray) would call -retain on rep in the right place.

Why is your code incorrect? Let's imagine two PRImage objects stored at addresses 0xDEADBEEF and 0xBADC0DE. 0xDEADBEEF has refcount 1 and is the current value of the bitRep ivar. 0xBADC0DE has just been passed as the rep variable, also with refcount 1.
[0xDEADBEEF release];
// deadbeef refcount is now 0 so it gets deallocated
bitRep = 0xBADC0DE;
[0xBADC0DE retain];
// badcode refcount now 2 

Or if rep == bitRep:
[0xDEADBEEF release];
// deadbeef released
bitRep = 0xDEADBEEF;
[0xDEADBEEF retain];
// crash!

'if bitRep != rep' should be enough to ensure this doesn't happen. For sake of code cleanliness, I'd still fix it as above.

Or (as stated) preferably I'd get rid of the PRImage subclass.

On Sun, Nov 30, 2014 at 2:27 PM, Riccardo Mottola <riccardo.mottola@libero.it> wrote:
Hi,

I have updated PRICE's (http://sourceforge.net/projects/price/) PRImage to keep a reference to my local image representation.

Here you can see the source code:
http://price.cvs.sourceforge.net/viewvc/price/PRICE-osx/PRImage.m?view=markup

In initWithData I simply do:
  self = [super initWithData:data];
  bitRep = [[self representations] objectAtIndex:0];
  [bitRep retain];

My copyWithZone implementation is as following>
- (id)copyWithZone:(NSZone *)zone
{
  PRImage *objCopy;

  objCopy = [super copyWithZone:zone];
  while( [[objCopy representations] count] > 0 )
    {
      [objCopy removeRepresentation:[[objCopy representations] objectAtIndex:0]];
    }
  objCopy->bitRep = nil;
  [objCopy setBitmapRep:[[bitRep copy] autorelease]];

  return objCopy;
}

I suppose it is a fine idea to invalidate all representations and then re-adding a copy of the principal one.

Copying is used during Undo, when the old image gets saved o during Redo.

This work fine on OS-X. I tried both 10.4 (ppc) and 10.6 (intel). However on GNUstep I get a crash (see below).
The crash happens when the old image gets released.

Undo has 1 level, I just have a copy of the old image. I release it before putting the next one in.

Why does it crash?
I have two guesses:
1) NSBitmapImageRep's copyWithZone doens't work correctly or as on Mac
2) NSImage copy is different/buggy? I do a superCopyWithZone
3) GS retains/releases its representations differently.

It could also be a problem in PRICE's code, perhaps in the copy code and that it works "by chance" on the mac

Riccardo

#0  0xb76872f3 in objc_msg_lookup ()
   from /usr/lib/gcc/i686-pc-linux-gnu/4.8.3/libobjc.so.4
#1  0xb7c33f7c in -[NSBitmapImageRep dealloc] (self=0x8231a88,
    _cmd=0xb7af1dd0 <_OBJC_SELECTOR_TABLE+304>) at NSBitmapImageRep.m:622
#2  0xb785382c in -[NSObject release] (self=0x8231a88,
    _cmd=0xb7f28050 <_OBJC_SELECTOR_TABLE+912>) at NSObject.m:2102
#3  0xb7cb7738 in -[GSRepData dealloc] (self=0x822ec28,
    _cmd=0xb7af1dd0 <_OBJC_SELECTOR_TABLE+304>) at NSImage.m:117
#4  0xb785382c in -[NSObject release] (self=0x822ec28,
    _cmd=0xb7aa5ca0 <_OBJC_SELECTOR_TABLE+96>) at NSObject.m:2102
#5  0xb7728aa3 in -[GSArray dealloc] (self=0x81815a8,
    _cmd=0xb7af1dd0 <_OBJC_SELECTOR_TABLE+304>) at GSArray.m:137
#6  0xb785382c in -[NSObject release] (self=0x81815a8,
    _cmd=0xb7f28050 <_OBJC_SELECTOR_TABLE+912>) at NSObject.m:2102
#7  0xb7cb7966 in -[NSImage dealloc] (self=0x8311268,
    _cmd=0x8099460 <_OBJC_SELECTOR_TABLE+960>) at NSImage.m:417
#8  0x0806678e in -[PRImage dealloc] (self=0x8311268,
    _cmd=0xb7af1dd0 <_OBJC_SELECTOR_TABLE+304>) at PRImage.m:60
#9  0xb785382c in -[NSObject release] (self=0x8311268,
    _cmd=0x8079d08 <_OBJC_SELECTOR_TABLE+1032>) at NSObject.m:2102
#10 0x0804ba43 in -[MyDocument saveCurrentImage] (self=0x839e330,
    _cmd=0x8079db8 <_OBJC_SELECTOR_TABLE+1208>) at MyDocument.m:366
#11 0x0804cd9d in -[MyDocument runFilter:with:] (self=0x839e330,
    _cmd=0x809d2f8 <_OBJC_SELECTOR_TABLE+856>, filter=0x84591a8,
    parameters=0x0) at MyDocument.m:300


_______________________________________________
Discuss-gnustep mailing list
Discuss-gnustep@gnu.org
https://lists.gnu.org/mailman/listinfo/discuss-gnustep



--
Ivan Vučica
ivan@vucica.net

reply via email to

[Prev in Thread] Current Thread [Next in Thread]