Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

GFLPraxis

macrumors 604
Original poster
Mar 17, 2004
7,152
460
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!
 
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.
 
Code:
NSLog
Logs an error message to the Apple System Log facility.

 void NSLog (
   NSString *format,
   ...
);

Ref:http://developer.apple.com/iphone/l...rence/reference.html#//apple_ref/c/func/NSLog

So, use it as a formatted string.

Code:
NSLog(@"%@", test); //Otherwise the application will probably crash here due to signal 10 (SIGBUS)



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.
 
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...
 
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:
test = nil;
or by doing nothing at all.


EDIT:

Exactly which book?

It's not "release/renew", it's "retain/release".
 
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.
 
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?

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.
 
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? :)
 
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? :)

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
 
But didn't I own the object at chdown33's // point A, because I initialized it?
Apple's Memory Management Rules said:
You take ownership of an object if you create it using a method whose name begins with “alloc”
 
But didn't I own the object at chown33's // point A, because I initialized it?

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.
 
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.

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.
 
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 an array of NSString* instances, you have one 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 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.

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."]; .

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.

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?


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.
gnasher said:
In three months time it will be obvious to you that this statement is asking for trouble.
@ 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?
 
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?
 
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?

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.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.