Set property on an allocated UIViewController - objective-c

I created a simple UIViewController with a custom init method like this:
-(id)initWithGroupNumber:(NSString *)groupNumber {
if ((self = [super init]) == nil) {
return nil;
}
self.levelGroup = groupNumber;
return self; }
levelGroup is a property written in the .h file
#property (nonatomic, retain) NSString *levelGroup;
When I call the method above this way:
LevelsViewController *lvc = [[LevelsViewController alloc]initWithGroupNumber:#"5"];
the controller is allocated but all the property inside are set to nil. I can't understand why.

First of all when you deal with classes that have subclass of type mutable (e.g. NSMutableString), use copy.
So, your property should become:
#property (nonatomic, copy) NSString *levelGroup;
Then, inside the UIViewController synthesize the property
#synthesize levelGroup;
and in init do the following:
-(id)initWithGroupNumber:(NSString *)groupNumber {
if (self = [super init]) {
levelGroup = [groupNumber copy];
}
return self;
}
As written in the memory management guide you should not use self. inside init and in dealloc.
Use your property self.levelGroup to get or set the value.
Remember to release in dealloc:
[levelGroup release];
Hope it helps.

Replace your
self.levelGroup = [[NSString alloc]init];
with
self.levelGroup = groupNumber; // actually uses your init value.

Related

Is the object retained when setting a property's ivar directly in -init?

My understanding is that instance variables should be accessed directly from inside the init method. For example:
#interface ABC : NSObject
#property (strong, nonatomic) NSString *name;
#end
#implementation ABC
- (id)init
{
if ((self = [super init]) != nil)
{
_name = #"some name";
}
}
// another init example
- (id)initWithName:(NSString*)n
{
if ((self = [super init]) != nil)
{
_name = n;
}
}
#end
I am wondering about the _name variable. In both init examples, is _name retained? For this example, I am using ARC.
Whether _name is retained in this code depends on whether you have ARC turned on. If you do, ARC will retain the object for you (since that is ARC's job). If you don't have ARC turned on, you need to retain it yourself, which would look like:
- (id)initWithName:(NSString*)n
{
if ((self = [super init]) != nil)
{
_name = [n retain];
}
}
(It's also worth pointing out that NSStrings should usually be copied rather than retained, so you would make the property #property (copy, nonatomic) NSString *name; and the assignment would be _name = [n copy].)

Singleton set in AppDelegate loses it value when allocated in another class

I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.

Why need I add self when assign attibute for one model?

I have one model.
I want to update one attribute each 3 seconds after init, and I need to change the attribute in one other view controller later.
the code is:
MODEL
#interface Ap : NSObject
#property (nonatomic, retain) NSString *address;
#property (nonatomic, retain) NSString *sessionId;
+ (id)sharedInstance;
#end
#implementation Ap
#synthesize sessionId, address;
-(id) init {
if (self = [super init]) {
self.address = nil;
self.sessionId = nil;
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(heartbeat) userInfo:nil repeats:YES];
}
return self;
}
-(void) updateSession{
sessionId = #"some string";
}
- (void) update{
self.sessionId = #"some value from network";
}
+ (Ap *)sharedInstance {
if (!sharedInstance) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
#end
CONTROLLER
- (void) viewDidLoad {
[super viewDidLoad];
[[Ap sharedInstance] updateSession];
}
The error is: (lldb) bad access when model update
And I change the updateSession method to
-(void) updateSession{
self.sessionId = #"some string";
}
The error is gone, can anyone tell me why?
You should use self.ap = [[Ap alloc] init]; in your app delegate. Your model object might be getting released when you try to call update on that which might cause a crash.
Your code will look like this,
#interface SomeAppDelegate : UIResponder{
#property(nonatomic, retain) Ap *ap; //declare property here
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if(!self.ap){
self.ap = [[Ap alloc] init]; //use property here as self.ap which will retain ap. Otherwise it will not retain it.
}
}
Update:
Looks like you have removed the previous question and added a completely new question to the previous one. Anyways I will try to answer this one as well.
The error is gone, can anyone tell me why?
The reason is same as what I mentioned above. If you use self.sessionId = #"some string";, you are retaining the object. Because it is a property and a property will internally retain since you have declared that property as retain. But if you use sessionId = #"some string";, its scope is only inside that method since you are not using the property. You are directly setting the value without calling property and it will autoreleased after that method. And hence you will get a bad access.
I would suggest you to go through the apple documentation to understand more about properties and its working.

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