NSString property and custom init - objective-c

I have 2 questions.
First - Are string declared as such in obj-c
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *city;
Are those (nonatomic, copy) right or should I use (nonatomic, strong), or something else ?
Second - If I want to set custom initializer for above strings do I use
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = n;
self.city = c;
}
}
or should I use:
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = [n copy]
self.city = [c copy];
}
}
As I can see both ways seem to work for both questions but I'm sure one is more correct than the other, so I wanted to ask which should I use to write correct code in further projects.
Thanks.

You want to use copy for value-semantic-typed properties. (Of which NSString is always one.) Otherwise, I can pass you an instance of NSMutableString and then change it out from under you after the fact. Immutable objects will implement -copyWithZone: by doing return [self retain]; so as to not actually create a second copy when it's not needed. See also: NSString property: copy or retain?
In terms of your -init method, you want to avoid using the property setters like you have since they could be overridden in a subclass to do something unforeseeable by your class. So assuming default auto-ivar-synthesis naming pattern, you would want to do this:
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
if (self = [super init])
{
_name = [n copy];
_city = [c copy];
}
return self;
}
It's a subtle thing, and it won't often be a problem, but if you continue to use inherently-virtual property setters in -init and -dealloc methods, over a long enough timeline, you will get burned by this (can you tell I've been burned by this?)
As for leaked memory, if you're using ARC, doing something like self.foo = [bar copy]; when foo is a copy property will result in copy getting called twice, and potentially two copies being made, but ARC should take care of properly releasing the redundant/intermediate copy, and there should not be a memory leak.

First - it doesn't really matter if you use copy or strong. Personally i prefer to copy strings rather than retain them
Second - `
-(id)initWithName:(NSString *)n andCity:(NSString *)c
{
self = [super init];
if (self) {
self.name = n;
self.city = c;
}
}`
You don't need to use copy message once more cause your properties will copy n and c. If you apply your second piece of code you'll copy n and c twice and have a memory leak

Related

How can I pass a NSString parameter to a function?

I want to initialize an object. The problem is how to pass the NSString correctly.
Object code:
#import "ClaseHoja.h"
#implementation ClaseHoja
#synthesize pares;
#synthesize nombre;
-(id)init
{
self=[super init];
if(self){
}
return self;
}
-(id)initWithValues:(NSString*)nom par:(int)par
{
if([super init]){
pares=par;
nombre=nom;
}
return self;
}
When I call the function I do this:
NSString *nombre="Hello";
int par=20;
ClaseHoja *ch = [ClaseHoja alloc] initWithValues:nombre par:numPares]];
I would suggest:
Add the missing # to #"Hello" and fix the [] in your alloc/init call.
If you're using Xcode, I'd let the compiler synthesize the properties for you. No #synthesize is needed. If you're using a stand-alone LLVM on some other platform, though, you might need it, but by convention, you'd specify an ivar with a preceding _.
I'd define nombre to be copy property and explicitly copy the nombre value passed to your init method. You don't want to risk having a NSMutableString being passed to your method and having it unwittingly mutated without your knowledge.
I'd suggest renaming the initWithValues:par: to be initWithNombre:pares:, to eliminate any doubt about what properties are being updated.
You don't need init without parameters. You can just rely on the one provided by NSObject.
You'd generally use NSInteger rather than int.
In your custom init method, you want to make sure to do if ((self = [super init])) { ... }
Thus:
// ClaseHoja.h
#import Foundation;
#interface ClaseHora: NSObject
#property (nonatomic, copy) NSString *nombre;
#property (nonatomic) NSInteger pares;
- (id)initWithNombre:(NSString*)nombre pares:(NSInteger)pares;
#end
And
// ClaseHoja.m
#import "ClaseHoja.h"
#implementation ClaseHoja
// If you're using modern Objective-C compiler (such as included with Xcode),
// you don't need these lines, but if you're using, for example stand-alone
// LLVM in Windows, you might have to uncomment the following lines:
//
// #synthesize nombre = _nombre;
// #synthesize pares = _pares;
- (id)initWithNombre:(NSString*)nombre pares:(NSInteger)pares {
if ((self = [super init])) {
_pares = pares;
_nombre = [nombre copy];
}
return self;
}
#end
And you'd use it like so:
NSString *nombre = #"Hello";
NSInteger pares = 20;
ClaseHoja *ch = [[ClaseHoja alloc] initWithNombre:nombre pares:pares];
You need to pass like this. Another thing you miss # sign before the string.
NSString *nombre = #"Hello"; int par=20;
ClaseHoja *ch = [[ClaseHoja alloc]initWithValues:nombre par:par];

Is that the correct way to deal with ivars?

I have read a lot of topics about getters and setters. I know what they are and why are useful. Different source claim's different ways to release the ivars. Here begins my confusion
#interface CoolClass : NSObject
{
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[self.name release], self.name = nil;
}
#end
Is that the correct way to release/free ivars ?
You'll want to use accessors most of the time, but not in partially constructed states because they can have negative side-effects. Here's how it's done:
- (id)init
{
if((self = [super init])) {
// self.name = #"Jo"; << don't use accessors in initializer
_name = [#"Jo" copy]; << good
}
return self;
}
// added for another variation:
- (id)initWithName:(NSString *)inName
{
if((self = [super init])) {
_name = [inName copy];
}
return self;
}
- (void)dealloc
{
// don't use accessors in dealloc
// don't release the result of a getter (release the result of the retained or copied result)
// [self.name release], self.name = nil;
// instead:
[_name release], _name = nil;
[super dealloc]; << compiler should have warned you about this one
}
Note: In the case of init, the string literal is an immortal and it won't matter if you copy it because the copy just returns itself. My preference is to just 'copy' the immortal for clarity, although it's unnecessary.
Here is what I would advise:
#interface CoolClass : NSObject
#property (nonatomic, copy) NSString *name;
#end
#implementation CoolClass
#synthesize name = _name;
-(id)init
{
if(super = [self super])
{
self.name = #"Jo";
}
return self;
}
-(void)dealloc
{
[_name release];
[super dealloc];
}
#end
Notes:
There is no need to explicitly declare ivars inside { ... } in your header. They will be created automatically when you synthesise your property. Explicit ivars are a legacy concept that are no longer needed since about iOS 3.
You should not use self.name in the dealloc as this calls the getter method, which may do additional work beyond merely fetching the ivar. Normally it's good practice to use the getter method, but in the dealloc you should release the ivar directly
It is good practice to set ivars to nil after releasing them, but again in the dealloc this in not necessary because no code is ever executed after dealloc, so the pointer won't be referenced again.
Normally (outside of the dealloc), if you wish to release an ivar you should set it to nil using the setter like this: self.name = nil; that will automatically release it and set it to nil. This is equivalent to [_name release], _name = nil;
#interface CoolClass : NSObject
{
NSString *name;
}
You declared here an instance variable 'name'; Nowadays there is no need to declare ivars in the header file. Just use properties and make the compiler to synthesize ivar for you.
#property (nonatomic, copy) NSString *name;
Here we have a property declaration that specifies that a copy of the object should be used for assignment and that a previous value is sent a release message.
In implementation you want to synthesize your property:
#synthesize name = _name;
This code tells the compiler to generate a getter and setter for property called 'name' and use instance variable called '_name' to store value. So you have now two ivars - 'name' and '_name'.
That how init method should like like:
-(id)init
{
if(self = [super init])
{
name = #"This is ivar declared between {}";
_name = #"synthesized ivar";
}
return self;
}
And the dealloc:
-(void)dealloc
{
[name release];
[_name release];
[super dealloc];
}

a puzzle on Objective-C memory management

This is one segment of codes used in one of my project for managing one of my class instance:
#interface C: NSObject
{
NSMutableArray *myArr;
}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
#implementation C
#synthesize myArr;
//...
-(id)init
{
//...
myArr = [[NSMutableArray alloc] init];
//...
}
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
here the version 1 dealloc method doesn't work and Xcode says something like "EXC_BAD_ACCESS..." and the app crashed.
if I modified the dealloc method as version 2, it works.
Does anybody have any idea why?
Thx in advance.
As Duncan said, the EXEC_BAD_ACCESS error means that the object doesn't exist anymore.
This is probably due to the fact that myArr is being released before the dealloc gets called.
To facilitate memory management and to keep track of reference counts, I like to make it clearer in the init methods, for example:
-(id)init
{
//...
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:0];
self.myArr = array;
[array release];
//...
}
By using the generated setter self.myArr = array; you are ensuring that the reference count is being delt with correctly, it will release the old value and retain the new one for you.
#MadhavanRP : if the property is a retained property:
#property(nonatomic, retain) NSMutableArray *myArr;
calling
self.myArr = nil
is exactely the same as calling
[myArr release];
myArr = nil;
Edit: #Sylvain beat me to it :)
This is OK even if it's bad idea/confusing to have same name for iVar and property. I removed the iVar declaration.
#interface C: NSObject
{}
#property (nonatomic,retain) NSMutableArray *myArr;
//...
#end
Generate your iVar using #synthetize.
#implementation C
#synthesize myArr = _myArr;
//...
Your init is all wrong. You are assigning the iVar without using the setter method.
-(id)init
{
//...
// You were doing
// _myArr = [[NSMutableArray alloc] init];
// You should do
NSMutableArray array = [[NSMutableArray alloc] init];
self.myArr = array;
[array release];
// You could avoid the temporary variable but this confuse XCode static analyzer
//...
}
This is OK. I guess that #synthetize generated another iVar to back your property.
This other iVar was not properly assign.
You would not notice this if you do not user self.myArr (or the equivalent [self myArr]).
This confusion is main reason why hardcore ObjC programmer do not like the new property thing. :) This confuse new programmers.
//version 1 of dealloc method
-(void)dealloc
{
//...
self.myArr = nil;
//...
}
This is not good as you bypass the setter, as in init method. This was working anyway.
//version 2 of dealloc method
-(void)dealloc
{
//...
[myArr release];
//...
}
It looks like your myArr object is being messaged when it should not be, in the first case you hide the problem by setting it to nil, in the second you don't. Either method should not crash. When do you call [super dealloc]; (you should call it at the end of the dealloc implementation).

NSMutableString returning (null)

What's wrong with this? The NSMutableString returns (null).
.h:
NSMutableString *aMutableString;
...
#property (assign) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)aMethod {
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
if (someCondition) {
[[self aMutableString] appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", [self aMutableString]);
[[self aMutableString] release];
}
First of all, your code has a couple of problems. First, you should define your aMutableString #property as retain, not assign. assign is generally for primitive, non-object types, like ints, etc., and for some special cases of objects. You appear to want to take ownership of aMutableString in such a way that it persists after the event loop returns. In your posted code, you end up accomplishing that because of how you incorrectly set the aMutableString in the following line:
[self setAMutableString:[[NSMutableString alloc] initWithString:#"message: "]];
By creating an NSMutableString with alloc/init, you're creating a potential memory leak situation, though in your situation, it actually makes up for your defining the property as assign rather than retain.
Your second -anotherMethod is also potentially dangerous in that:
1) it releases an instance variable you defined as assign
2) after releasing it, it doesn't set it to nil. If you try to access that instance variable elsewhere in that class at a later point in time, you will likely get a crash because the pointer is no longer valid, if the instance variable has been dealloced.
So, the code should most likely look something like this:
.h
NSMutableString *aMutableString;
...
#property (retain) NSMutableString *aMutableString;
.m:
#synthesize aMutableString;
- (void)dealloc {
[aMutableString release];
[super dealloc];
}
- (void)aMethod {
[self setAMutableString:[NSMutableString stringWithString:#"message: "]];
if (someCondition) {
[aMutableString appendString:#"woohoo"];
}
}
- (void)anotherMethod {
NSLog(#"%#", aMutableString);
// the following is potentially unsafe!
// [[self aMutableString] release];
// it should be one of the following:
[aMutableString release]; aMutableString = nil;
// or
// [self setAMutableString:nil];
}
That said, without more information, it's a little hard to say what the problem is. I assume you mean the NSLog() call is printing (null)? If so, that means that aMutableString is still nil. Are you calling -aMethod before calling -anotherMethod?
If you want to make sure that aMutableString is initialized to an empty string, you could override -init:
- (id)init {
if ((self = [super init])) {
aMutableString = [[NSMutableString alloc] init];
}
return self;
}
Make sure you have #synthesize aMutableString; in your .m file

I wonder about releasing variables

UIView *view; //1
UISegmentedControl *scopeBar; //2
NSMutableArray *array; //3
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) UISegmentedControl *scopeBar;
#property (nonatomic, retain) NSMutableArray *array;
.m
#synthesize view, scopeBar, array;
for (id subView in [view subviews]) {
if ([subView isMemberOfClass:[UISegmentedControl class]]) {
scopeBar = (UISegmentedControl *)subView;
}
}
array = [[NSMutableArray alloc] init];
- (void)dealloc {
}
I think that only the third of the variables has to be released in the dealloc method.
Is that right?
Yes, (array needs to be released) because you alloc it. So, it's programmer's responsibility to release it. So -
- (void)dealloc {
[ array release ] ;
// Any other resources alloc, init, new should be released
}
For more info on what to release, Memory management - ObjectiveC
And I think you will find good suggestions in this question about your query
Why should we release?
Contrary to some of the answers, you have to release your outlet (view) as well, and not only in the dealloc but also in the viewDidUnload, the easiest way is to set it to nil :
self.view = nil;
Also note that if you don't access your properties but your instance variables (i.e. without self. prefix) your retain attribute won't help you and you are not retaining the object. That means that as soon as scopeBar would be removed out of the subViews of the view, it will be released and you end up accessing a zombie.
As a rule of thumb, it's best to use the properties accessor everywhere except in the init methods so that you don't have to deal with the memory management explicitly. Setting them to nil in the dealloc and viewDidUnload in case of outlets should be enough then.
Also, don't do what Jenifer suggested and once you've called a release on a variable, don't set the property to nil, that would overrelease it.
I think that only the third of the variables has to be released in the dealloc method. Is that right?
// no. your dealloc should look like this:
- (void)dealloc {
// note: *not* using accessors in dealloc
[view release], view = nil;
[scopeBar release], scopeBar = nil;
[array release], array = nil;
[super dealloc];
}
// your assignment of `scopeBar` should look like this:
...
self.scopeBar = (UISegmentedControl *)subView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `view` should look like this:
...
self.view = theView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `array` should look like this in your initializer:
// note: *not* using accessors in initializer
...
// identical to `array = [[NSMutableArray alloc] init];`
array = [NSMutableArray new];
...
// and the assignment of `array` should look like this in other areas:
...
self.array = [NSMutableArray array];
...
// you're likely to be best suited to declare your array as
// follows (assuming you really need a mutable array):
...
NSMutableArray *array; // << the declaration of the ivar
...
...
// the declaration of the public accessors.
// note the array is copied, and passed/returned as NSArray
#property (nonatomic, copy) NSArray *array;
...
// finally, the implementation manual of the properties:
- (NSArray *)array {
// copy+autorelease is optional, but a good safety measure
return [[array copy] autorelease];
}
- (void)setArray:(NSArray *)arg {
NSMutableArray * cp = [arg mutableCopy];
// lock? notify?
NSMutableArray * prev = array;
array = cp;
[prev release], prev = nil;
// unlock? notify? update?
}
other answers assume that dangling pointers (e.g., you still hold a pointer to view, although the view may have changed behind your back) are allowable.
they should not be allowed in real programs. they are extremely dangerous, and it can very difficult to reproduce errors they cause. therefore, you must ensure you own a reference to the pointers you maintain/hold.
you should also use the accessors in the public interface for the subclasser's sake - in case they override them. if you don't want to allow/support that, consider simply using a private variable.
As i think you should release and set them nil because you have made them properties so do this:-
in your dealloc
[array release];
self.array=nil;
self.scopeBar=nil;
self.view=nil;