Change the value of NSString, am I doing it right? - iphone

I have created a class as follows
#interface sampleClass: UIViewController
{
NSString *currentLocation;
}
#property (nonatomic, copy) NSString *currentLocation;
So, whenever the current GPS changes, a function will be called as follows:
-(void)newLocationUpdate:(NSString *)text {
NSString *temp = [[NSString alloc] initWithString:text];
currentGPS = temp;
[temp release];
}
I am wondering if I am doing it right? Thanks.

Ignoring the currentLocation/currentGPS confusion — no, that's still not quite right.
You don't show the the setter for currentLocation. I'll assume it's a #synthesized property. If you just write currentLocation = something, you're not invoking the property setter; you're just setting the instance variable. This means that after you release the object in the very next line, your instance variable is probably pointing to a deallocated object.
The correct way to write it (again, assuming you have a synthesized accessor) would be:
-(void)newLocationUpdate:(NSString *)text {
self.currentLocation = text;
}
This invokes the property accessor, which copies the object for you.
If for some reason you needed to access the instance variable directly (like if this were the setter method for currentLocation), you would write:
-(void)newLocationUpdate:(NSString *)text {
[currentLocation release];
currentLocation = [temp copy];
}

If you have your currentLocation/currentGPS setter implemented properly (with #synthesize or a manual implementation), then you're doing too much work. If you have a property declared with the copy flag, as you do, then the setter method itself will do the copy that you're doing by hand. All you would need is this line:
[self setCurrentGPS:text];
Or, if you prefer the property syntax:
self.currentGPS = text;
That will automatically call the copy method, which is basically a more efficient way of doing what you're doing the long way with [[NSString alloc] initWithString:text].

Here:
#interface sampleClass: UIViewController
{
NSString *currentLocation;
}
-(void)newLocationUpdate:(NSString *)text;
.m
-(void)newLocationUpdate:(NSString *)text {
currentLocation = text;
}
That's the way I would do it. Is it correct? Probably not. Will it work? Yes.

Nope, you're doing it wrong.
Your class sampleClass has an ivar named currentLocation and a property named currentLocation. In your newLocationUpdate method you are setting a variable named currentGPS to the string value passed to that method. As written, currentGPS has no relationship with either the variable currentLocation or the property currentLocation.
Also, your use of init and release in your newLocationUpdate method looks like you may have a fundamental misunderstanding of how memory management in Obj-C works. You should definitely read Apple's guidelines on memory management.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
This is important stuff! Good luck.

Related

objective-c initializer in memory-management environment

So I'm trying to write an initializer for a subclass of NSOperation. This is my first time using NSOperation. My NSOperation subclass looks like this:
.h
#property (nonatomic, copy) NSString *fileName;
.m
#synthesize fileName = _fileName;
- (id)initWitFileName:(NSString *)fileName {
if (self = [super init]) {
_fileName = fileName;
}
return self;
}
- (void)dealloc {
[_fileName release];
[super dealloc];
}
- (void)main {
// do long task
}
So I thought when I create my own initializers, I should just set the ivar myself since the state of the object is undetermined in the init method. So I don't use the accessor in the initializer. When my main method gets run, I get a memory bad access and a crash. However, in my init method, if I do this instead:
- (id)initWitFileName:(NSString *)fileName {
if (self = [super init]) {
_fileName = [fileName retain];
}
return self;
}
I do not get a crash. What is correct? I'd think in this second case that I wouldn't be releasing the memory since the accessor is (copy). Or is it because I don't use the accessor, that my fileName object basically gets dealloc'd only since there is no +1 in the initWithFileName method? Thanks!
The second one is close, but:
- (id)initWitFileName:(NSString *)fileName
{
if (self = [super init]) {
_fileName = [fileName copy];
}
return self;
}
is more usual since NSString* properties are generally defined with (nonatomic, copy).
This is done to avoid problems or unintended consequences if the caller is actually passing a NSMutableString and later changes the value.
When you #synthesize something with a set of properties, you're generating getters and setters for the object. This allows you to use methods like
[self setFilename:#"MyFile.txt"]
or
self.filename = #"MyFile.txt"
Both of the lines above function in exactly the same way, and might be implemented like this:
- (void)setFilename:(NSString *)string {
_filename = [string copy];
// Or, for a retained property:
// _filename = [string retain];
}
This implementation is overly simplified. See idz's comment below for a better understanding of the logic that a real setter would use.
In your example above, you're simply using the = operator, and not a generated function.
_fileName = fileName;
Because of this, you're simply setting the address of _fileName to fileName. None of your synthesize properties matter, and don't make a difference here.
Your explanation:
Is it because I don't use the accessor, that my fileName object basically gets dealloc'd only since there is no +1 in the initWithFileName method?
is exactly correct. To properly replicate the copy property used by your synthesizer, use this:
_fileName = [fileName copy];
Idea 1: Use ARC. In ARC, you'd declare your ivar strong, and then either use the synthesized setter or assign the ivar directly. A retain will be added for you and all will be well.
Idea 2: Use retain. If you're unable to use ARC for some reason, why you don't declare the NSString as retain, instead of copy? It's okay to hold onto the same instance of an immutable thing. (Sharing a mutable thing can be useful, too, as long as both classes understand that they are doing that).
Idea 3: If you must use non-arc and you must use copy, set the ivar with copy. That will fix your crash by doing the copy. As it is now, the code is copying the handle to the passed string, but not it's contents. The passed string is dealloc'd and your class is stuck with a zombie. You can say _filename = [string copy]; as others have suggested.
As a side-note, calling self = [super init]; is correct inside the overridden init method. Since you're making your own init variant, I think it's more proper to call self = [self init];

Avoid Memory Leak When Property Assigning Twice

Let say i have an class named as MyTestClass.h.
Class structure is look like
#interface MyTestClass : NSObject {
NSString *testString;
}
#property (nonatomic, retain)NSString * testString;
#end
.m file
#implementation MyTestClass
#synthesize testString;
-(id) init{
[self setTestString:#""];
return self;
}
-(void)dealloc{
[self.testString release];
testString = nil;
[super dealloc];
}
#end
Now i created an object of MyTestClass and assigned testString twice
MyTestClass * myTestClass = [[MyTestClass alloc] init];
[myTestClass setTestString:#"Hi"];
[myTestClass setTestString:#"Hello"];
Now i think, two times my testStrings memory is leaked!! (one through init() and another one through my first setTestString method)
Am i correct? or will #property (nonatomic, retain) handle/release previous allocated memory?
or ,in this kind of cases ,will i need to override the setTestString() in MyTestClass.m like below code
-(void)setTestString:(NSString *)tempString{
[testString release];
testString = nil;
testString = [tempString retain];
}
Any help on this question is appreciated.
Thanks.
Any help on this question is appreciated.
I'll take this as a licence to make sone observations not necessarily directly related to your question.
Firstly, if you declare a retain property (as you have done) and synthesize it, the automatically generated getters and setters handle memory management correctly for you.
If you manually create setter (which you are allowed to do even with an #synthesize existing), you have to do the memory management yourself. Use either of trojanfoe's examples.
The setter in your question contains a bug in that if testString == tempString i.e. you assign the value of the property to itself, you could end up with assigning a dangling pointer to the property because you effectively release tempString and then retain it.
This is an implementation detail that you an safely ignore, but string literals e.g. #"blah" are compiled into the executable and will never be deallocated no matter how many times they are released. So, with your example, even if the setter did not do correct memory management, there will be no leak.
By the way, the normal pattern for an init method is
-(id) init
{
self = [super init];
if (self != nil)
{
// init stuff
}
return self;
}
or logical equivalent.
You should get into the habit of using it because you need to call the super class's init method and it is allowed to change the value of self, even to nil.
Also, while it is very good practice normally to set the object reference to nil after releasing it, in both cases when you do it, it is unnecessary. the first time, the variable is about to go out of scope and the second time you immediately assign it from some other object.
It's not a leak. Synthesized variable are correctly handled.
A synthesized method is implemented in this way (for a retain keyword)
#property (nonatomic, retain) NSString *string;
//backed by variable NSString *_string;
- (void)setString:(NSString*)newString
{
if (newString != _string) {
[_string release];
_string = [newString retain];
}
}
Of course this is a leak:
- (void)aMethod //of my class with string property
{
NSString *aString = [[NSString alloc] initWithString:#"hello"];
self.string = aString; //retain count of 2
self.string = #"hello2"; //retain count of 1 for aString
//now I don't release aString.... leak
}
If you use the auto-generated setter (in your case, setTestString:, which is also called by self.testString = ...;), the previous value of a retain property is released before being set. So no, there is no leak in the code you posted above.
The synthesized setter method should do the right thing. Here's an example of it's implementation:
- (void)setTestString:(NSString *)tempString
{
[tempString retain];
[testString release];
testString = tempString;
}
or:
- (void)setTestString:(NSString *)tempString
{
if (tempString != testString)
{
[testString release];
[tempString retain];
testString = tempString;
}
}
the dealloc is only called when the instance is destructed.
if you do :
[myTestClass setTestString:#"Hi"];
[myTestClass setTestString:#"Hello"];
in the same block, you're juste calling twice the setter. there is no memory leak.
When you use #synthesize on a property that specifies retain, the setter that's generated will handle the retain/release correctly for multiple assignments. As long as you use self. rather than going directly to the backing variable and do a final release in dealloc you should be fine.

Autorelease and properties

I have few questions to ask about the following class
#import <Cocoa/Cocoa.h>
#interface SomeObject {
NSString *title;
}
#property (retain) NSString *title;
#end
implementation SomeObject
#synthesize title;
-(id)init {
if (self=[super init])
{
self.title=[NSString stringWithFormat:#"allyouneed"];
}
return self;
}
-(void)testMethod{
self.title=[[NSString alloc] init] ;
}
-(void)dealloc {
self.title=nil;
[super dealloc];
}
In the .h file do we need to declare the title and sub when we add the property. is it not enough to add the #property (retain) NSString *title; line.
2.Do i need to autorelease both assignment to title in the init and testMethod. if So why?
Can some one explain these things to me.
1-
You don't need to declare the iVar in the header. You might also use
#synthesize myVar = _myVar;
if you want to go for a different iVar name
2-
Declaring a property "retain" means that every time you assign the property with a new object, it automatically releases the previous object and retain the new one.
Therefore, if you use a convenience method like stringwithFormat, the property will retain that object for you.
If you want to use alloc-init, for me the best way to do is:
NSString *str = [NSString alloc] init];
self.title = str;
[str release];
Besides, it is right to assign nil to the property in the dealloc because the property will release the object it has, and it calls retain on nil which doesn't do anything
1.No need to declare title in .h, declaring property is enough.
2.when you are using self.title in init, you do not have to autorelease it.But when you initialize it in testMethod, you need to autorelease it because you have declare the property as retain.And do not forget to release title in dealloc.
you don't need to add as it is done automatically (Since Xcode 4 I guess).
in init- you don't as it already returns an autoreleased object..
where as in testMethod you need to since you are allocating it..
you always have to release any object which you create using alloc , copy or new .... AMEN.. :)
Be aware it is not considered a good practice to use accessor methods in initializer methods and dealloc method. Do check out this answer: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?
Also in Apple's memory management guide: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html

Crash when accessing a NSMutableArray member variable

I'm having some trouble with a NSMutableArray. I'm sure i'm doing something wrong with the allocation of the NSMutableArray but it's not obvious to me being an iPhone newbie. When i run the code below i can add the object MyObject to the array objects_ and set the name etc. The NSLog displays the correct data.
But when i try to access the objects_ member from the function printObject i get a SIGABRT. Looks like the memory has been deallocated or something?
Any help appreciated.
#interface MyObject : NSObject {
NSString *name;
}
-(void) SetName:(NSString*) name_str;
-(NSString*) GetName;
#end
#interface ObjectListViewController : UITableViewController {
NSMutableArray* objects_;
}
-(void) initTableData;
#end
#implementation ObjectListViewController
- (void)initTableData {
objects_ = [[NSMutableArray alloc] initWithCapacity:10];
MyObject *obj = [MyObject alloc];
[obj SetName:#"Test"];
[objects_ addObject:obj];
MyObject* testObj = (MyObject*)[objects_ objectAtIndex:0];
NSLog([testObj GetName]);
}
- (void)printObject {
MyObject* testObj = (MyObject*)[objects_ objectAtIndex:0];
NSLog([testObj GetName]);
}
We can eliminate the lack of an init call on MyObject as the cause of the crash as in this case it will be benign. Calling init on NSObject will just return self, so calling it in this case won't change the behaviour. So I don't think the first two answers here will make any difference:
An object isn’t ready to be used until it has been initialized. The init method defined in the NSObject class does no initialization; it simply returns self.
Chuck correctly points out that init is a fundamental step in object allocation and initialization and you should be calling it when you allocate MyObject.
I am not sure the third answer is correct either. I don't really see how adding synthesise on the objects_ array will make any difference. You haven't defined it as a property, and I don't really see why you would need to, given it is just data internal to the class.
The comment on the question Well, for starters, you never define printObject in the #interface. from eykanal doesn't really help you either, because you must be calling printObject internally, otherwise you wouldn't be hitting the crash.
Reading the through the code, I can't see an obvious error. The retain count on objects_ after initTableData finishes should be one, the retain count on the instance of MyObject should also be one. So I think there must be some other code that is releasing objects_ elsewhere?
I am assuming it is crashing on the objectAtIndex call? Is there any info in the console? What does the call stack look like?
MyObject *obj = [MyObject alloc];
should be:
MyObject *obj = [[MyObject alloc] init];
#interface ObjectListViewController : UITableViewController {
NSMutableArray* objects_;
}
#property (nonatomic, retain) NSMutableArray *objects_;
-(void) initTableData;
-(void) printObject;
#end
add the synthesize in the implementation
#implementation ObjectListViewController
#synthesize objects_;
Here are some issues in your code:
You never initialise your MyObj object. Although it inherits directly from NSObject and NSObject is documented to do nothing except return self, you never know if other stuff happens behind the scenes, so put it in just to eliminate the posssibility.
Your methods don't follow the normal naming conventions. method names should begin with a lower case letter and "get" should only be used when passing back data by reference through the parameters as in e.g. NSData -getBytes:length:. Your getter and setter should be -name and -setName: respectively. This may seem like a minor nitpick, but it'll help you later on if you start to use KVO and KVC.
Never do NSLog(someStringVariable) always NSLog(#"%#", someStringVariable). As you have it now, if the object's name contains a percent formatting sequence e.g. %#, %d, %s etc, your program will crash on the NSLog. However, this is not the cause of your current problem - it would be crashing on the NSLog in -initTableData
you don't need to cast the result of -objectAtIndex:
Having said all that, I can't see anything that would cause the particular issue you have. It may be that the getter or setter for the name in MyObject is incorrect. Please post them.

use NSString in other method?

It's really embarrassing but i stuck on it for two hours of trial and error.
I declared an NSString in the interface as:
NSString *testString;
Then i made a property and synthesized it. I allocate it in viewDidLoad with:
testString = [[NSString alloc] initWithFormat:#"thats my value: %i", row];
If i want to get the value of the string in another method it always return (null).
So the string is empty, but why? how do i make it accessible for every function inside of one class? So i don't want to make a global var just a "global variable inside the class"
It's really confusing because as long as i code i never ran into this problem :(
thanks a lot for helping me!
In your interface, declare the property:
#property (nonatomic, readwrite, retain) NSString *testString;
In the implementation, synthesize it:
#synthesize testString;
In the implementation, add a release to the -dealloc method:
[self.testString release];
In -viewDidLoad, access the property:
self.testString = [[[NSString alloc] initWithFormat:#"that's my value: %i", row] autorelease];
I personally use
self.testString = [NSString stringWithFormat:#"my value: %i",row];
That way you don't need to worry about releasing it.
Also make sure you always use "self".
you can try to retain your string in viewDidLoad. but if it will work for you, don't forget to release it one more time
All other answers have you directly or indirectly retaining testString. However since you get testString with an alloc and an init, it is already owned by you without needing to be retained again.
I'm thinking your problem has to do with something else. You are either prematurely releasing testString in some method, or your viewDidLoad did not get called when you were trying to access your testString in other methods.
In order for you to use a variable defined in your implementation, you don't have to declare it a #property and #synthesize it. Those can help, but you don't have to have them. You may have to show us more code if this problem doesn't go away.