New to Cocoa; having some trouble with NSString.

Discussion in 'Mac Programming' started by GFLPraxis, Jun 11, 2010.

  1. GFLPraxis macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #1
    Hi; if someone couple help the newbie out in understanding how NSString works, I'd most appreciate it :eek:

    I built a simple app; it has a TextView and a Button, and a Listing object. The Listing object contains a NSString named test, an init method, and a displayText method.

    displayText runs when I press the button I created in Interface Builder, and outputs text to the TextView, and then outputs the contents of the NSString to NSLog.



    Listing.h:
    Code:
    #import <Cocoa/Cocoa.h>
    #import "Character.h"
    
    
    @interface Listing : NSObject {
    
    	IBOutlet NSTextView *textView;
    	NSString *test;
    	
    }
    
    - (IBAction)displayText:(id)sender;
    
    @end
    Listing.m:
    Code:
    #import "Listing.h"
    
    @implementation Listing
    - (id)init
    {
    	[super init];
    	NSLog(@"Listing initialized.");
    	test = [NSString stringWithFormat:@"testing text."];
    	
    	return self;
    }
    -(IBAction)displayText:(id)sender
    {
    	[textView insertText:@"lol test\n"];
    	[b]NSLog(test);[/b]
    
    	return;
    }
    
    @end
    
    This application freezes and then crashes when I press the button. When I comment out the bolded text, it works fine. If I use @"testing text." instead of test, it works fine. So, clearly, I can't use a NSString object just like that.

    What's the proper usage?


    Thanks all!
     
  2. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
  3. rev316 macrumors regular

    Joined:
    Nov 7, 2004
    #3
  4. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #4
    Alright, I added
    Code:
    test = [[NSString alloc] init];
    as the second line in the Listing init method. The app no longer crashes, but NSLog(test); says "Format not a string literal and no format arguments". It compiles, but nothing is ever logged.
     
  5. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #5


    New code:

    Code:
    #import "Listing.h"
    
    
    @implementation Listing
    - (id)init
    {
    	[super init];
    	test = [[NSString alloc] init];
    	NSLog(@"Listing initialized.");
    	test = [NSString stringWithFormat:@"yomomma."];
    	
    	return self;
    }
    -(IBAction)displayText:(id)sender
    {
    	[textView insertText:@"lol test\n"];
    	NSLog(@"%@", test);
    	//[textView insertText:(NSString)test];
    	return;
    }
    
    @end
    App still crashes when I hit the button.
     
  6. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #6
    Please read the memory management rules again, then once or twice more.
     
  7. Thomas Harte macrumors 6502

    Joined:
    Nov 30, 2005
    #7
    You might want to read up on the assignment operator too.
     
  8. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #8
    Would someone be so kind as to be a little more specific? I'm working through a book, and toying with code like this on the side to get a better understanding. I understand the release/renew model, but I have no one to ask questions to. If someone can show me specifically what I am doing wrong, I can then go back to the book and figure out what is lacking in my understanding.

    Thanks...
     
  9. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #9
    Code:
    @implementation Listing
    - (id)init
    {
    	[super init];
    	test = [[NSString alloc] init];
    	[COLOR="Red"]// point A[/COLOR]  
    	NSLog(@"Listing initialized.");
    	test = [NSString stringWithFormat:@"yomomma."];
    	[COLOR="red"]// point B [/COLOR] 
    
    	return self;
    }
    
    At point A, who owns the string assigned to the ivar 'test'?

    At point B, who owns the string assigned to the ivar 'test'?

    At each point, if the owner is the caller, i.e. the instance of Listing that assigns the object to its ivar 'test', then what is the owner responsible for doing?

    At each point, if the owner is NOT the caller, then what is the caller responsible for doing? If the caller doesn't do what it should, then what is the lifetime of the object assigned to 'test'?

    If you don't know what ownership means, then you should read the Memory Management Guide whose URL was already provided. Ownership and non-ownership is fundamental.


    And on a separate note, please explain the purpose of:
    Code:
    	test = [[NSString alloc] init];
    
    that is not better served by:
    Code:
    	test = @"";
    
    or:
    or by doing nothing at all.


    EDIT:

    Exactly which book?

    It's not "release/renew", it's "retain/release".
     
  10. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #10
    Hmm. I added [test retain]; to the init method, and the code is now working.

    I assume that means it was being released at some point between its creation and me running NSLog- at the end of the init method seems the most logical time.

    But, doesn't the string get a retain count of 1 when it is created? Don't I have to manually release to reduce the count? Why is it being deallocated without me doing it manually?

    EDIT: Posted same time as above post, one moment to review.
     
  11. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #11
    You need to understand the "convenience methods" such as the one you were using to initialize the string. These create "autoreleased" objects that will be deallocated when the autorelease pool is drained at the end of the main runloop (shortly after your method exits). You can only trust the persistence of objects created using +alloc, +new, -copy, or and method that specifically states in its documentation that a retained object is being returned.
     
  12. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #12
    renew was a typo, sorry :eek: release/renew is what I use with ipconfig on Windows xD

    I'm using Beginning Mac Programming, Develop with Objective C and Cocoa by Tim Isted.

    I'm only about 1/4 of the way through it so far, and coming from Java + a little bit of C experience.

    chown33, I used that line to allocate/initialize it because it's a little more general purpose and I'd used it on other objects in the book, and because the above posts told me it was a memory issue, so I added that in to see if it changed anything- in my original code, I didn't use it.

    I think I got it. My mistake was that I didn't realize that the object is autoreleased at the end of the method that created it. So, I had to manually increase the count to 2, so it wouldn't hit zero at the end of that method and I could use it later. Right? :)
     
  13. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #13
    don't ask if the count is 2. Ask if you own the object or not. If you don't, don't expect the object to stick around. If an object is autoreleased, it's not yours. People were telling you to understand the memory management rules. This was because they explain clearly when you own an object returned and when you don't.

    -Lee
     
  14. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #14
    But didn't I own the object at chdown33's // point A, because I initialized it?
     
  15. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #15
    You misunderstand the distinction between an object (or instance) and a variable.

    A variable holds an object. An object (not the variable) is either owned or unowned. The variable itself is neither owned nor unowned, it's just a variable holding an object reference (specifically, an object reference is fundamentally a C pointer).

    For example, if you have a C array of NSString* instances, there is one named variable that holds multiple object references. Any particular array element can be either owned or unowned, or it can be nil which is neither but which does respond to messages. The C array variable as a whole has no specific owned or unowned state.

    At point A, you owned the object assigned to (i.e held by) 'test'. You then assigned another object to 'test', which you did NOT own. So at point B, the resulting contents of 'test' is NOT owned. It's only the ultimate contents or value of the variable that matter later on.

    Also, you neglected to release the owned object in 'test' before assigning another object. That's a leak. It may not show up as one due to the nature of NSString and its optimizations, but you nevertheless have a logic error in failing to release an owned object.

    EDIT:
    BTW, if you'd done this:
    Code:
    test = @"yomomma.";
    
    you would not have had a problem. String constants have infinite lifetime within the context of a program (i.e. same as C's 'static' lifetime). You can retain, release, or autorelease string constants as often as you like, they never go away.
     
  16. gnasher729 macrumors P6

    gnasher729

    Joined:
    Nov 25, 2005
    #16
    Listen to catfish and do as he told you. Alternatively, please post as precisely as you can with as much detail as possible what is the precise effect of the statement

    Code:
    test = [NSString stringWithFormat:@"testing text."];
    In three months time it will be obvious to you that this statement is asking for trouble. Following the link that catfish gave and reading it carefully will take you a very, very big step forward. And do you remember how printf () works? If you think back at printf, that should explain the warning that the compiler gave you.
     
  17. GFLPraxis thread starter macrumors 604

    GFLPraxis

    Joined:
    Mar 17, 2004
    #17
    Alright, I'm getting it. I think my mistake here is in failing to realize that I do not own the data that is returned by [NSString stringWithFormat:mad:"testing text."]; .

    I was aware of the leak, but since I only created one listing object and had no intention of ever creating more than one, and would be reusing it later down the line, I decided to leave it as is.

    What would be the best way to account for that? Would it be to override dealloc with a dealloc that releases test, and then calls super's dealloc?


    @ gnasher, surprisingly, I learned that from the book...hm. Maybe they'll tell me not to do it later down the road.

    If my understanding is correct, it sends the string "testing text." to a method described in NSString, which returns a string formatted in a way I assume NSString likes. I suppose, in this case, that it is redundant.

    So- is the reason it is asking for trouble is because I'm having an external method create the NSString, and thus, I don't own it, and because I don't own it, it is set to autorelease?
     
  18. peterf303 macrumors newbie

    Joined:
    May 31, 2010
    #18
    I'm just learning Objective C too, so reading through this thread made me realise that I've been calling stringWithFormat (and variations) in the same way but I haven't had any problems with the object getting lost.

    why? It drove me mad for a good few minutes before I realised that I've been setting up @property accessors for all my NSStrings

    So, I think what GFLPraxis needs to do is have

    @property (readwrite, copy) NSString *test; in the header and @synthesize test; in the source file.

    This means any access to his NSString *test will copy the object and own it. Is this correct?
     
  19. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #19
    Almost. As I understand it, for the @property to work the way you want it to, you must use accessors (including dot-syntax). For example, if the "test" variable above were @synthesized as a property and then accessed directly as a variable the way the code is written above, the problem would persist, because directly accessing the property's ivar would bypass its declared attributes with raw C.

    self.test = [NSString stringWithFormat:mad:"yomomma"];

    would work, but

    test = [NSString stringWithFormat:mad:"yomomma"];

    would still fail for the same reason and would have the same potential to leak. It may be possible that the compiler is or will become more aggressive about generating accessors for @properties, so I could be wrong.
     

Share This Page