1

I've tried asking a couple people I know but they were probably too busy to give me at advice on this.

I keep running into walls with the way alloc/dealloc are dealt with. The following one is driving me nuts... this is a boiled down essence of some code I have been trying to debug all week.

Here is what I think should be happening (but obviously is not):

  • I allocate a pool
  • I create an object and autorelease to put it in the current pool so it will dealloc when I release the pool.
  • The test class has an NSString var that is only internal.
  • The NSString is released in the dealloc method of that class.
  • I release the pool, which does try to release the test object,
  • It segfaults in the dealloc method as soon as it tries to release the NSString.

In the past I've managed to get buy by fiddling until the problem went away. However I have now reached the point of development where I need to be able to prove I have eliminated storage issues.

I have read masses of documentation over the last year on ObjC storage management. Unfortunately, there seems to be some disagreement in how this all actually fits together, and that confusion is not helpful to those of us who have not been part of this managed storage process as it grew. If I am having difficulties (I used to write DSA's in assembly code for RTOS's), I can only imagine what others must be facing.

One thing that would be really useful is a manual on how to test your code to eliminate this sort of fault. I am sure such information exists, and have run across a bit here and a bit there, but the pieces don't all fit together neatly. I've also been using enableDoubleReleaseCheck.

I left it out to keep the test case simple.

Here's CommandTest.m, a complete test case that results in:

./CommandTest
Release cmdline
Segmentation fault (core dumped)


/* Makefile strings:
   OBJC = gcc -g -fobjc-exceptions -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS -I /usr/include/GNUstep

   CommandTest:    CommandTest.o $(OBJC) -g -o CommandTest CommandTest.o -lobjc -lgnustep-base */

#include <stdio.h>

import

@interface Command:NSObject {NSString *cmdline;}
- (id)   init;
- (void) dealloc;
@end

@implementation Command
- (id) init {
  if (!(self = [super init])) {return nil;}
  cmdline = [NSString stringWithCString: "This is a test"];
  return self;
}

- (void) dealloc {
  printf ("Release cmdline\n");
  [cmdline release];
  printf ("Release cmdline done\n");
  [super dealloc];
}
@end

int main( int argc, char *argv[] ) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  Command *cmd            = [[Command new] autorelease];

  @try {[pool release];}
  @catch (NSException *e){
    printf ("Fatal [%s] %s\n", [[e name] cString], [[e reason] cString]);}

  printf ("Done\n");
  exit (EXIT_SUCCESS);
}

Advice would be most welcome and much appreciated.

Dale Amon
  • 219
  • 2
  • 5

2 Answers2

2

Your string is being double-released. +[NSString stringWithCString:] creates an autoreleased object. It is automatically put into the nearest autorelease pool.

When the pool is released, the string is too. Then, the string's owner sends release again, which causes your exception. (These two may actually happen in either order; it doesn't matter.)

From the point of view of the reference count, your Command object doesn't actually own the string, because it has not followed the NARC rule. It has not used any of new, alloc, retain, or copy on the string. The autorelease pool is the sole owner. Thus, it's an error for the Command to later send release.

The solution is to send retain to the autoreleased string when you create it -- then both the pool and the Command would have ownership, and would be required to send release later -- or to create it using alloc, as in [[NSString alloc] initWithCString:]. Then the Command would be the sole owner.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • The scary part of that is... how do you know when an object created for internal use in a Class is already autoreleased or retained and when it is not? I'd thought the general rule was that if you allocate it, you release it. – Dale Amon Feb 03 '14 at 21:29
  • By convention. If you create it with `new` or `alloc`, it's an owned reference. If you don't, then it's not your concern (but as a practical matter it's almost certainly in the autorelease pool). The rule is if you allocate it _using `alloc`_. – jscs Feb 03 '14 at 21:31
  • It looks like the relevant section of GNUStep's docs is here: http://www.gnustep.org/resources/documentation/Developer/Base/ProgrammingManual/manual_3.html#SEC43 – jscs Feb 03 '14 at 21:33
  • Thanks. Using that modified mnemonic, I will start building my way back up to the original, far more complex code. In the original an array of strings created by a split is joined into a new line with a join and that line was then stored. I used the initWithCString to replicate the segfault in a simpler context. I will probably be back with more questions later so please bear with me. I really need to get the logic of this burned into me. I've got a rather large and mission critical project with a tight time line. Of course that's probably where all of you sit day to day as well. – Dale Amon Feb 03 '14 at 21:41
  • Apple also has good writeups: //developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html Glad I could help. Good luck. – jscs Feb 03 '14 at 21:44
  • The alloc approach dies with NSInvalidArgumentException, reason: GSPlaceholderString(instance) does not recognize stringWithCString: but a retain does work in this case. – Dale Amon Feb 04 '14 at 00:25
  • You can try clang ARC, and GNUstep mailing list. – Fred Frith-MacDonald Feb 04 '14 at 23:43
1

Problem is here:

cmdline = [NSString stringWithCString: "This is a test"];

You're not retaining the object, so it's effectively returned with a 0 retain count and autoreleased soon after.

mipadi
  • 398,885
  • 90
  • 523
  • 479