Problem passing Strings

Discussion in 'Mac Programming' started by klaxamazoo, Mar 15, 2011.

  1. klaxamazoo macrumors 6502

    Joined:
    Sep 8, 2006
    #1
    I am having an odd problem that I can't figure out. I am trying to fill a table with values parsed from some file names.

    I am trying to fill up a tableView using the standard:
    Code:
    tableView:(NSTableView *)aTableView	
        objectValueForTableColumn: (NSTableColumn *) tableColumn 
                              row: (int) row 
    method.

    However, I get an error when I return the value depending on how I set the value eblTestname in rowEblTest.eblTestName.

    Code:
    -(id)               tableView:(NSTableView *)aTableView	
        objectValueForTableColumn: (NSTableColumn *) tableColumn 
                              row: (int) row 
    
    NSLog(@"CALLED - AppDelegate: objectValueForTableColumn");
        NSLog(@"FOR: tableColumn = %@", [tableColumn identifier]);
        
        NSString *tableIdentifier = [tableColumn identifier];
        NSLog(@"tableIdentifier = %@", tableIdentifier);
        
        //eblTest Table
        if (aTableView == tableView_eblTest) {
            NSLog(@"Tableview == EBL Test");
            NSLog(@"row = %i, ebltTestArray count = %lu",row, [eblTestArray count]);
            if (row <= ([eblTestArray count])) {
                NSLog(@"passEBLTestArray size check with:");
                // create local EBLTest to handle variables
                EBLTest *rowEblTest = [eblTestArray objectAtIndex: row];
    
    
                // This is where I have the error depending on how eblTestName is defined:
                NSLog(@"%@", rowEblTest.eblTestName);
                return [rowEblTest valueForKey: tableIdentifier];
            }
        }
    The problem I have is when I set the eblTestName. The TableView loads just fine if I set the eblTestName directly with an arbitrary testString1 I made directly; however, but I have a memory problem is I use testString2 which comes from a custom NSObject, ImageTag, which I use to parse the filename and then pass the values between my different objects.

    Code:
    // initialize EBLTest object
    -(id) initWithBlank {
    	self = [super init];
        NSLog(@"CALLED: EBLTest - initWithBlank");
        ImageTagging *newImageTag = [[ImageTagging alloc] init];  // Initializes default ImageTag
        
        
    // testString1 is hard-coded, arbitrary string
        NSString *testString1 = [NSString stringWithString: @"EBL-TA1"];
        NSLog(@"TESTSTRING = %@", testString1);         // This outputs fine
      
    // testString2 is taken from a newImageTag
        NSString *testString2 = [newImageTag valueForKey:@"eblTestName"];
        NSLog(@"TESTSTRING2 = %@", testString2);        // This outputs fine
        
        // testString1 sets eblTestName correctly
        // testString2 results in a memory error once eblTestName is called from my AppDelegate
    	eblTestName = [NSString stringWithString: testString2];
        
        NSLog(@"eblTestName = %@", eblTestName);        // This outputs fine
        
        dosage = 680;
        
        sampleGroupArray = [[NSMutableArray alloc] init];
    	
    	return self;
    }
    I think I have a problem with pointers or something but I can't figure it out.
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    You have a memory management bug. Specifically, you have a "lack of ownership" bug.

    The red-hilited code is almost certainly the problem. I say "almost" only because you didn't show any headers, so I can only guess that eblTestName is an instance variable.

    The problem is you're storing a reference to an object you don't own. Since you didn't claim ownership, then at some later time the object will dealloc'ed, but your eblTestName still has a pointer to it. That is, eblTestName will eventually point to a dealloc'ed object. Grave disorder ensues.

    http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

    The solution is to claim ownership for as long as your class needs that object to exist. You must also relinquish ownership when you don't need that object in that specific class.


    Your other option on Mac OS is to enable garbage collection (GC) for your app.
     
  3. klaxamazoo, Mar 15, 2011
    Last edited: Mar 24, 2012

    klaxamazoo thread starter macrumors 6502

    Joined:
    Sep 8, 2006
    #3
    Thanks you so much. I added a [testString2 retain] and it worked just fine. I thought that @property (copy) NSString would give me a copy of eblTestName when I set its value. I'm going to review the memory management and then make a few small, test programs to double-check my understanding.

    Thanks,
    Bob
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    It will only do that when you use property syntax:
    Code:
    self.eblTestName = someString;
    
    or when you use setter-method syntax:
    Code:
    [self setEblTestName:someString];
    
    The syntax you used was this:
    Code:
    eblTestName = someString;
    
    which always and only means direct variable access. Direct variable access provides none of the property's declared attributes.
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    I've made a habit of using setters and getters in init methods to maintain the encapsulation, even though you're already in code for the object. This way when you have properties the attributes you've given them will still be used. Those attributes are meaningless if you assign directly to an ivar with = instead of self.myProperty = or [self setMyProperty:...].

    You should still review the memory management guide, but if you did things this way you would be able to depend on copy behaving as you expected.

    -Lee

    EDIT: Beaten to the punch.
     

Share This Page