I'm using UISearchBar, and one of its properties, text, is declared as follows:
Abstract: The current or starting search text.
Declaration: #property(nonatomic, copy) NSString *text;
I know that the rule is to release what ever you use +alloc, +new or -copy.
I did:
NSString *searchText = searchBar.text;
And:
[searchText release];
And I got a nice EXC_BAD_ACCESS message. When I removed the release line, the EXC_BAD_ACCESS message stopped to appear, so I assumed that it is the eror source.
The question: Shouldn't I release searchText, since it comes from a property that uses copy?
No, you should not use release here. The "copy" in this case refers to how the setter is implemented, not the getter. The call you made (-text) does not include the word "copy" so you should not release the result.
The copy attribute of the property means that the object is copied before assigning to the instance variable. When you access this property you then get a reference to the copy that was made.
When you set the the text on the searchbar:
NSString* myTextString = [[NSString alloc] initWithString:#"My Text String"];
mySearchBar.text = myTextString;
[myTextString release];
To elaborate on Rob Napier's correct answer:
NSString *searchText = searchBar.text;
This code assigns a reference to the text property of searchBar to searchText. This is not a copy of the searchText, just another reference to the same NSString object in the searchBar object. Releasing searchText is the same as releasing searchBar.text, which cause your EXC_BAD_ACCESS message.
In this declaration of the text property, the getter method is merely:
- (NSString *)text {
return text;
}
The more interesting method is the setter method. For this declaration, the setter is similar to:
- (Void)setText:(NSString *)newString {
if (text != newString) {
[text release];
text = [newString copy];
}
}
Related
When inserting an object into an array with a property is there any reason to invoke the getter/setter with self? i.e.
[self.myArray insertObject: myObject];
Or can I just use:
[myArray insertObject: myObject];
the gist would be:
.h
#interface ArrayViewController : UIViewController <UITextFieldDelegate>
{
NSMutableArray *myArray;
int itemNumber;
}
#property (nonatomic, retain) NSMutableArray *myArray;
#end
.m
- (IBAction)createMyArray
{
self.myArray = [[NSMutableArray alloc] initWithObjects: nil];
}
-(IBAction) addItemToMyArray
{
NSString *myString = [[NSString alloc] initWithFormat:#"item %d",itemNumber];
[myArray addObject: myString];
//[self.myArray addObject: myString]; //Or should I use self?
[myString release];
NSLog(#"myArray = %#", myArray);
itemNumber++;
}
//- (void)dealloc etc. not shown
Conceptually, it doesn't matter, so long as your getter method only returns the existing field value and doesn't, eg, do some "just in time" allocation or some such.
However, it's good practice to come up with a policy (personal or group) that you stick with, so that the caveats of that policy become second nature. Constantly switching styles results in sloppy, buggy code.
I tend to always use the self. for properties, just to remind myself that they are, in fact, properties, and to make it less likely that I'll accidentally set the value without using the property notation.
Either will work but you need to be aware of what you are doing. Using self. will invoke the setter/getter methods while the other will just access the variable directly. Using the variable directly, while perfectly valid, is discouraged outside of the initializer and dealloc method. The reason is you are losing out on the benefits of the property, especially setting using self. because it will properly assign/copy/retain the value for you correctly. Another reason not use property variables directly is because of atomicity but in your case you declared it as nonatomic.
Both of those are fine. It's mostly a stylistic choice. Using self.myArray will result in a call to the getter [self myArray].
When using alloc/init you should not set the returned value to a property, as these will retain twice:
self.myArray = [[NSMutableArray alloc] initWithObjects: nil];
use
myArray = [[NSMutableArray alloc] initWithObjects: nil];
or
self.myArray = [NSMutableArray array];
for the initialization.
The insert operations are equivalent though.
I typically skip the getter because I rarely find it valuable and it clutters up the readability of the code a bit. However, I tend to use the setter because I find it easier to allow the auto-generated setter methods to handle the retain/release semantics
In your case it's not an obligation to use self.myArray but for this case belloaw it will be an obligation:
-(void) addItemToMyArray:(NSAarray *)myArray
{
NSString *myString = [[NSString alloc] initWithFormat:#"item %d",itemNumber];
[self.myArray addObject: myString];
[myString release];
NSLog(#"myArray = %#", self.myArray);
itemNumber++;
}
to difference between the class attribut and the function argument.
I have the following code in my .h file:
#interface Utils : NSObject {
NSString *dPath;
}
#property(nonatomic, retain) NSString *dPath;
And in my .m file:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
dPath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent:kDatabaseName];
[dPath retain];
Why do I have to retain dPath if it's already defined as (nonatomic, retain)?
If I don't add the [dPath retain]; I get some strange, random errors and the application crashes when using this variable in other functions. I guess that's because of some autorelease somehere but I don't have any.
So, what is the (nonatomic, retain) doing anyway? Is it really necessary the [dPath retain]; or am I just hiding something else with that?
Because the code isn't calling the dPath property's setter method, it's just setting the instance variable dPath directly:
dPath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent:kDatabaseName];
[dPath retain];
So it has to be retained manually.
You will be able to (in fact you need to) omit the retain call if the property setter was used like this (notice the self.):
self.dPath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent:kDatabaseName];
or like this (notice the setDPath:):
[self setDPath:[[documentPaths objectAtIndex:0] stringByAppendingPathComponent:kDatabaseName]];
The setter retains the NSString for you so you don't have to do it yourself.
A nice little practice to follow in order to avoid the confusion, is to affix an underscore to your ivar name to indicate it's an ivar:
NSString *dPath_;
Then synthesize your property like this, to associate it with your differently-named ivar:
// self.dPath is the property, dPath_ is the ivar
#synthesize dPath = dPath_;
Then modify your dealloc method, as well as any other code that directly references the instance var, to use the affixed name instead:
- (void)dealloc {
[dPath_ release];
[super dealloc];
}
try setting and getting it with
self.dPath
If you want to call the property setter method, which will invoke the retain, then you want to write:
self.dPath = ...
Jamming stuff into a variable with:
dPath = ...
completely ignores the properties of this instance variable. Which is why you ended up needing to do the retain manually.
in the following piece of code I got from a book.
The NSString *pPath which is defined in the class as an instance variable.
#interface MainViewController : UIViewController {
NSString *pPath;
}
In the implementation after being set it is being retained. I assume that with the assignment the object is automatically retained (because it is an NSString) and there is no need to additionally retain it.
- (void) initPrefsFilePath {
NSString *documentsDirectory =
[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
pPath = [documentsDirectory stringByAppendingPathComponent:
#"flippingprefs.plist"];
[pPath retain];
}
Yes, you need to retain your pPath variable if you obtain it this way. However it is not the end of the story - you also need to release its previous value otherwise it will just leak.
To make things easier you can use objective-c properties that allow you to automatically generate setter/getter methods with desired memory management behavior:
// header
#interface MainViewController : UIViewController {
NSString *pPath;
}
#property (nonatomic, retain) NSString* pPath;
// implementation
#synthesize pPath;
- (void) initPrefsFilePath {
NSString *documentsDirectory =
[NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
self.pPath = [documentsDirectory stringByAppendingPathComponent:
#"flippingprefs.plist"];
}
Here in self.pPath=... line automatically generated setter method will get called which:
Will release previously set pPath value
Will assign new value to pPath and retain it
You will also need to release your pPath variable in dealloc method:
-(void) dealloc{
[pPath release];
//or
self.pPath = nil;
[super dealloc];
}
I have a table view that when loading creates a person object
Person.h
#import <UIKit/UIKit.h>
#import "TwitterHelper.h"
#interface Person : NSObject {
NSDictionary *userInfo;
NSURL *image;
NSString *userName;
NSString *displayName;
NSArray *updates;
}
/*
#property (retain) NSString *userName;
#property (retain) NSString *displayName;
#property (retain) NSDictionary *userInfo;
*/
#property (nonatomic, copy) NSURL *image;
#property (retain) NSArray *updates;
- (id)initWithUserName:userName;
#end
Person.m
#import "Person.h"
#implementation Person
/*
#synthesize userName;
#synthesize displayName;
#synthesize userInfo;
*/
#synthesize image;
#synthesize updates;
- (id)initWithUserName:(NSString *)user{
userName = user;
userInfo = [TwitterHelper fetchInfoForUsername:user];
displayName = [userInfo valueForKey:#"name"];
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
updates = [TwitterHelper fetchTimelineForUsername:userName];
return self;
}
- (void)dealloc
{
/*
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
*/
[super dealloc];
}
#end
Inside my UITableView method cellAtRowForIndexPath I am creating each person object and assigning the image property like so...
Person *person = [[Person alloc] initWithUserName:userName];
NSData *data = [[NSData alloc] initWithContentsOfURL:person.image];
[data release];
When I run this in Instruments it highlights the NSData *data... row saying that is where the leak is.
Why is it leaking there?
First, you need to understand the difference between instance variables and properties and getter/setters.
instance variables (ivars) are variables stored in
your object. You access an ivar from within a method simply by naming it (eg "userName").
properties define an
interface to your object, allowing
information to be read and/or written
to your object.
getters/setters implement that interface and may use an ivar as backing storage
You access a property by using a getter/setter, either explicitly (eg [self userName]) or (equivalently) using dot syntax self.userName. Note that these two notations are exactly identical. You declare a property (ie, you declare an interface to your object) using #property in the interface of your object, something like:
#property (copy) NSString* userName;
This declartion is essentially equivalent to typing:
- (NSString*) userName;
- (void) setUserName: (NSString*) theUserName;
You implement a property, either by using #synthesize (which simply tells the compiler to write the getter/setter for you) or by implementing it yourself (ie, you write methods implementation for userName and setUserName). There is also a rarely used third option, #dynamic, which tells the compiler you will handle the methods at run time, essentially just silincing the warning you would otherwise get.
Next, you need to read and understand the memory management rules. Its only 9 short paragraphs, go read it now, I'll wait. Done? good.
Further, you need to know that you should not use getters/setters in either the init or dealloc routines.
So your init routine should look something like this:
- (id)initWithUserName:(NSString *)user{
userName = [user copy];
userInfo = [[TwitterHelper fetchInfoForUsername:user] retain];
displayName = [[userInfo valueForKey:#"name"] copy];
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] copy];
updates = [[TwitterHelper fetchTimelineForUsername:userName] retain];
return self;
}
Note that you take ownership of each value you store in an ivar with retain or copy. Generally, you use copy for NSString to convert an NSMutableStrings into NSStrings you own, rather than retain which would leave you holding a reference to a possibly mutable string. The same issue applies to NSArray/NSDictionary, but we will assume TwitterHelper intends to hand off the fetched data.
Your dealloc will have to release the various ivars:
- (void)dealloc
{
[userName release];
[displayName release];
[updates release];
[userInfo release];
[image release];
[super dealloc];
}
Anywhere else in your code you would use self.userName to access or change the properties, rather than access the ivars directly.
Note that you might consider not storing the displayName (and similarly image) at all, but simply implement a property getter that retrieves it from userInfo. To do this, delete the displayName ivar, change the property to:
#property (readonly) NSString *displayName;
remove the #synthesize displayName, and add a manual getter:
- (NSString*) displayName
{
return [userInfo valueForKey:#"name"];
}
and remove the release in dealloc.
Note that you do not need to retain/release the value in displayName - you return a value that the receiver does not own and it is up to them to copy/retain it if they want to keep it.
If you choose to create a property, you should use:
self.image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
in your init message and not
image = [NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]];
Setting the value without the self prefix will not call the copy or retain message, and will create a memory problem (not necessarily a leak).
This might be what Instruments is pointing you to.
(This obviously applies to all properties!)
Alternatively, if you don't want to use the accessor, then retain or copy the value retrieved, e.g.:
image = [[NSURL URLWithString:[userInfo valueForKey:#"profile_image_url"]] retain];
You are calling alloc on Person but not releasing it. You've leaked your person object.
(in your cell configuration)
I just spent the last 3 hours trying to figure out this error. I would like someone to explain it to me so I don't do it again.
I assigned an NSString instance variable without using "self". When the class ("self") released, I received a "bad access" error. I have done this exact same thing in another class with the same variable declarations and do not have this error. Below is my code. I commented out the line that broke and the line below fixes it. But I don't understand why... Notice that there are other instance variables that do not cause this problem. Should I always use the "self" reserved word when assigning instance variables? Please let me know.
declarations
#property (nonatomic, readonly, assign) int IID;
#property (nonatomic, assign) int ProfileIID;
#property (nonatomic, retain) NSDate *NoteDate;
#property (nonatomic, copy) NSString *NoteText;
code snippet
// the default date format is Year-Month-Day
NSDateFormatter *df = [[[NSDateFormatter alloc] init] autorelease];
[df setDateFormat:kDateFormat];
IID = sqlite3_column_int(selectstmt, 0);
ProfileIID = sqlite3_column_int(selectstmt, 1);
// notice this does not cause a memory error
NoteDate = [[df dateFromString: [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)]] retain];
// the following may be NULL. Checking using the sqlite3_column_text method
const char *columnText = (const char *)sqlite3_column_text(selectstmt, 3);
if(columnText != NULL)
{
// this causes a memory error
//NoteText = [NSString stringWithUTF8String: columnText ];
// this does not cause memory error
self.NoteText = [NSString stringWithUTF8String: columnText ];
}
The reason that
NoteDate = [[df dateFromString: [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)]] retain];
is fine is because you retain the variable. Since you do not allocate the string, but call stringWithUTF8String on NSString, you do not take ownership of the variable, and so the string returned to you is autoreleased. However, since you retain it, this does not cause problems.
If variables are returned autoreleased, then they are released when the autorelease pool is drained, which occurs at the end of each event (see more on autorelease pools). This is no good with an instance variable, because it needs to stick around after the current event.
When you assign the variable by:
NoteText = [NSString stringWithUTF8String: columnText];
Your setter method is not invoked, so the returned string (which, again, is autoreleased) is not retained, and so is released by the autorelease pool at the end of the event.
Calling it as:
self.NoteText = [NSString stringWithUTF8String: columnText];
does retain the string, since the line is another way of writing:
[self setNoteText:[NSString stringWithUTF8String: columnText]];
which invokes your setter method and retains the variable, preventing it from being released at the end of the current event.
Assigning NoteText from within a class method doesn't invoke the synthesized setter method, it sets the instance variable directly. This means your string isn't being retained (or copied, in the case of your setter), and the crash is when trying to release an object which has already been released. Do this:
self.NoteText = [NSString stringWithUTF8String: columnText ];
This will call your setter, and all will be well.
Edit:
Just to be clear, this is true for all ivars.
myVariable = someValue; // Sets the ivar directly.
self.myVariable = someValue; // calls [self setMyVariable].
This confusion between ivar and setter methods is why I never name my setters and my ivars the same.
Apple typically names its ivars starting with an underscore (_), for example NoteText. In my case, I've taken to having a prefix of i for ivars. For example:
NSString* i_name;
#property (nonatomic, copy) NSString* name;
#synthesize name = i_name;
That way you can easily tell the difference between an ivar assignment:
i_name = [[whatever title] retain];
and a setter method call
self.name = [whatever title]; // Equivalent to [self setName:[whatever title]
The setter, since it is defined with copy (or similarly for retain) will take ownership of the variable passed in and release ownership of the old value. The ivar assignment does none of that.
Note also that your propery names should start with a lower case letter or they will not be KVO compliant.