iPhone SDK: How/when should I release a UITableView delegate object? - iphone

I am using a custom class as the delegate and datasource on a UITableView. I'm doing (something like) this in my viewDidLoad method:
MyClass *myObject = [[MyClass alloc] init];
tableViewOutlet.delegate = myObject;
tableViewOutlet.dataSource = myObject;
Surely I need to decrease the retain count on myObject somewhere? But calling [myObject release] here has very bad results - the delegate gets destroyed before the table has finished doing its stuff.
I have tried
MyClass *myObject = [[[MyClass alloc] init] autorelease];
but it also has terrible consequences.
Do I have a memory leak here? If so, how and when do I release the delegate safely?

Your interface file:
#interface SomeClass: NSObject {
MyClass *myObject;
}
#property (nonatomic,retain) MyClass *myObject;
#end
Your implementation file:
#implementation SomeClass
#synthesize myObject;
-(void)dealloc {
// if you want to be safe, change tableViewOutlet properties...
// tableViewOutlet.delegate = nil;
// tableViewOutlet.dataSource = nil;
[myObject release]; // retain = 0
[super dealloc];
}
...
MyClass *obj = [[MyClass alloc] init]; // retain = 1
self.myObject = obj; // retain = 2
// NOTE: if you instead write: myObject = obj; **NO** retain msg will be sent.
// *not* what you want in this context.
tableViewOutlet.delegate = obj; // assign, so retain =2
tableViewOutlet.dataSource = obj; // assign, so retain = 2
[obj release]; // retain = 1
...
#end

make myObject an instance variable by declaring it in the #interface (.h file), then call...
[myObject release];
.. in your dealloc method.

Related

Is only one release is needed in my Objective C code?

For some reason I can't use arc, so in my code below..
Foo.h
#interface Foo : NSObject
#property (nonatomic, copy) NSString * string;
#end
Foo.m
#implementation Foo
#synthesize string=_string;
- (void) bar {
self.string = [NSString stringWithFormat:#"test1"];
self.string = [NSString stringWithFormat:#"test2"];
}
-(void) dealloc
{
[_string release];
[super dealloc];
}
#end
The bar method might not always be called, or they can be called multitime.
Is only one release in the dealloc is all needed?
Yes, since you are using properties so setter method will take care of releasing memory allocation.It will allocate memory as follows:
-(void)setValue:(NSString *)strValue
{
if(string)
{
[string release];
string = nil;
}
string = [strValue copy];
}
Is only one release in the dealloc is all needed?
Yes.
Explanation: the setter method releases the old object that was assigned to the property and retains the new one.

NSMutableArray with memory leak

I am using following code to create NSMutableArray. When I run the same in “Profile” mode, it is showing a memory leakage.
SampleArray.h
#interface SampleArray: NSObject {
}
#property (assign, retain) NSMutableArray *array;
#end
SampleArray.m
#import "SampleArray.h"
#implementation SampleArray
#synthesize array;
-(void) viewDidLoad {
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[self.array release];
}
#end
When I am using autorelease, then I can’t able to access the same in other function or method and return null value. Please help me to find the issue.
releasing this array in viewWilLDisappear is not a good idea, you should release in the dealloc function. You should worry about over-releasing this item and causing a program crash since viewWilLDisappear may get called multiple times during the lifetime of this ViewController.
Anyhow, you are double retaining the item beacuse your property has a retain on it (and make it nonatomic, not assign), add an autorelease to your alloc/init:
self.array =[[[NSMutableArray alloc] init] autorelease];
and move
[array release];
to your dealloc function. Or convert to ARC and don't worry any longer...
Try setting it to (nonatomic, retain), then autoreleasing.
It is better to handle memory de-allocation in your -dealloc() and set your array to nil to be more secure in your -viewDidUnload()
so it will be:
-(void) viewDidUnload
{
self.array = nil;
}
-(void) dealloc
{
[array release];
[super dealloc];
}
and like other people said, declare your property as (nonatomic, retain) instead of (assign, retain)
First of all I'm assuming that you are using
#property (nonatomic, retain) NSMutableArray *array;
use this
-(void) viewDidLoad {
array =[[NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
[array release];
}
I will recommend you to use dealloc instead of viewWillDisappear
-(void) dealloc {
[array release];
[super dealloc];
}
Explanation of your code
-(void) viewDidLoad {
// here you are allocating a mutable array thus retain count becomes one
// then you are assigning it to the property which is retain and thus retains it
// making the retain count 2
self.array =[ [NSMutableArray alloc] init];
}
-(void) viewWillDisappear:(BOOL)animated {
// here you are releasing it so its retain count becomes 1 from 2
// thus shows memory leak
[self.array release];
}

Correct way to create/use a Singleton NSMutableArray for Xcode 4

I've reviewed (and tried) a bunch of the threads here regarding Singletons and NSMutableArrays. I'm new to Objective-C so please bear with me.
I simply want to create a few arrays that can be accessed from any view/.m file.
What is the best (or most concise) coding for a Singleton?
Below is what I have now and I get
1 warning at .m '#implementation' - "Incomplete implementation"
1 error at usage in a view .m file - "initializer element is not a compile-time constant"
This is the code I have now - my GlobalData.h file:
#import <Foundation/Foundation.h>
#interface GlobalData : NSObject {
NSMutableArray *listOfHeadings;
NSMutableArray *listOfItems1;
NSMutableArray *listOfItems2;
}
#property(nonatomic,retain)NSMutableArray *listOfHeadings;
#property(nonatomic,retain)NSMutableArray *listOfItems1;
#property(nonatomic,retain)NSMutableArray *listOfItems2;
+(GlobalData*)getInstance;
#end
My GlobalData.m file:
#import "GlobalData.h"
#implementation GlobalData
#synthesize listOfHeadings;
#synthesize listOfItems1;
#synthesize listOfItems2;
static GlobalData *instance=nil;
+(GlobalData *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [GlobalData new];
}
}
return instance;
}
#end
And in a view .m file (simplified):
#import GlobalData.h
GlobalData *globDat=[GlobalData getInstance]; //error occurs here
Can someone point out the trouble and if there's better coding, please enlighten me - thanks!
EDIT
Here's a few links I've tried to use:
Can i have a single NSMutableArray in my multiple views application?
iPhone help with singleton class
In this case, you might be doing more than you have to. Granted this certainly isn't always the best solution - but you can put your NSMutableArray as a property in your App Delegate and then easily refer to it from any view. By doing it this way - you aren't locking it in as a 'singleton' but there is a 'singleton instance' of it (this helps a great deal for testability).
I have simplified this process here:
YourAppDelegate.h
#property (nonatomic,retain) NSMutableArray *myArray;
YourAppDelegate.m
#synthesize myArray;
YourViewController.m
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *myArrayFromAppDelegate = appDelegate.myArray;
From this point - you can do any manipulation on this value.
Here's the "modern" version of a single method to turn any class into a Singleton (in this case formatted as a code snippet). It works in iOS4.x or higher:
+(<#SingletonClassName#> *) sharedInstance
{
static <#SingletonClassName#> *_sharedClient = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedClient = [[self alloc] init];
});
return _sharedClient;
}
But, do you really need a singleton of a single NSMutableArray? You could use the built-on singleton - your application delegate, which is got to by calling:
MyAppDelegate * appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.myMutableArray addObject:...];
The error initializer element is not a compile-time constant is not related to how you create your singleton. The error is how you are accessing your singleton. You are doing this outside of a function:
GlobalData *globDat=[GlobalData getInstance];
This means that you are trying to initialize a global variable (globDat) as the value of the expression [GlobalData getInstance]. You can only initialize global variables to expressions that are "compile-time constants". That means things like 0 or "fred" or 8/2. The value of [GlobalData getInstance] cannot be computed at compile-time, so it cannot be used to initialize the global variable.
Instead, you need to just use [GlobalData getInstance] inside your function bodies wherever you are currently trying to use the globDat variable.
As for the warning, Incomplete implementation, I don't see what's missing. Perhaps you didn't post all of the code from GlobalData.h. Anyway, you should be able to click the warning (where it appears on the right side of the editor window) and have Xcode show you what's missing.
This is the way I create my Singleton:
Singleton.h
#import <Foundation/Foundation.h>
#interface Singleton : NSObject {
NSMutableArray *firstMutableArray;
NSMutableArray *secondMutableArray;
}
#property (nonatomic, retain) NSMutableArray *firstMutableArray;
#property (nonatomic, retain) NSMutableArray *secondMutableArray;
+ (id)sharedSingleton;
#end
Sigleton.m
#import "Singleton.h"
static Singleton *sharedMySingleton = nil;
#implementation Singleton
#synthesize firstMutableArray;
#synthesize secondMutableArray;
#pragma mark Singleton Methods
+ (id)sharedSingleton {
#synchronized(self) {
if (sharedMySingleton == nil) {
sharedMySingleton = [[super allocWithZone:NULL] init];
}
return sharedMySingleton;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedSingleton] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
- (oneway void)release {
// Never release
}
- (id)autorelease {
return self;
}
- (id)init {
if (self = [super init]) {
firstMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
secondMutableArray = [[NSMutableArray alloc] initWithObjects:nil];
}
return self;
}
- (void)dealloc {
[firstMutableArray release];
[secondMutableArray release];
[super dealloc];
}
#end
Then, when you want to call your Singleton:
#import "Singleton.h"
Singleton *singleton = [Singleton sharedSingleton];
singleton.firstMutableArray = ...
singleton.secondMutableArray = ...

Passing a pointer by reference to class and using it

I am trying to pass a pointer by reference to an object from class A to class B. In class B I want to assign this pointer to a ivar and read and write to it.
This is the code that gives me errors (does not matter what errors). This is my first try with pointers so please correct my understanding.
Class A
//This is the parameter I would like to pass as a pointer and be able to manipulate from class B
NSString *name = #"Cyprian";
-(void)passAParameter{
ClassB *classB = [[ClassB alloc] initWithAPointer:&name];
...
}
Class B
// ClassB.h
#interface ClassB{
NSString **nameFromClassA;
}
#property(nonatomic,assign)NSString **nameFromClassA;
-(id)initWithAPointer:(NSString **)name;
// ClassB.m
#implementation ClassB
#synthesize nameFromClassA;
-(id)initWithAPointer:(NSString **)name{
*nameFromClassA = *name;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", *nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
*nameFromClassA = newName;
}
Please, do not use double pointers here. You shouldn't handle things like that.
This is a simpler approach:
In the ClassA instance:
-(void)passAParameter{
NSString *name = #"Cyprian";
ClassB *classB = [[ClassB alloc] initWithAPointer:name];
...
}
While you define ClassB this way:
ClassB.h:
#interface ClassB{
NSString *nameFromClassA;
}
#property(nonatomic,retain) NSString *nameFromClassA; // Retaining it will give you less headaches
-(id)initWithAPointer:(NSString *)name;
#end
ClassB.m:
#implementation ClassB
#synthesize nameFromClassA;
// You should release all retained object when deallocating self
- (void) dealloc {
[nameFromClassA release];
nameFromClassA = nil;
[super dealloc];
}
-(id)initWithAPointer:(NSString *)name{
if ((self = [super init])) { // Always init the object from super!
self.nameFromClassA = name; // Retain the object calling self.
}
return self;
}
//Print the name
-(void)printName{
NSLog(#"Name: %#", nameFromClassA);
}
//Will this change the name in class A?
-(void)changeNameInClassA:(NSString* newName){
self.nameFromClassA = newName; // Retain it calling self.
}
#end
The assignment in your initWithAPointer: method should be just:
nameFromClassA = name;
That said, this code pattern smells of a bad design. What high-level goal is it that you're trying to accomplish?

iphone - trying to understand #property

Suppose I have two classes. In the first one I declare this in Class1.h
#interface Class1 : UIViewController {
NSString *myString;
id myObject;
}
On the second class I go beyond that I declare it like
#interface Class2 : UIViewController {
NSString *myString;
id myObject;
}
#property (nonatomic, retain) NSString *myString;
#property (nonatomic, retain) id myObject;
and then I #synthesize myString, myObject on Class2.m
Then, on my main program, I create two objects: one based on Class1 and another one based on Class2.
What effect the #property of class2 will have? Will every value assigned to both values on Class2 be always retained? If so, do I need to "release" them? How?
Thanks.
Please read Declared Properties section of The Objective-C programming language
for a full explanation on properties ;)
In Class2:
In this case you set retain attribute to your property it is supposed to be retained in the implementation. This is done automatically when you synthesize a property.
This means that you should have
- (void) dealloc{
[myString release];
[myObject release];
[super dealloc];
}
and everything should be fine
In Class1, you don't have properties so myString and myObject is not visible from outside. But this does not mean that you shouldn't release them. It depends on the way you initialize them and/or if you send retain messages to them.
BTW, if you set assign a property you don't release it, just set it to nil in the dealloc method. If you set copy to it then you must release it.
EDIT
You said: *But suppose I have this *
#property (nonatomic, retain) UIView *myView;
and
myView = [[UIView alloc] initWithFrame:myFrame];
[self.view addSubview:myView];
[myView release];
? I am already releasing myView... do I have to release it again???
First, since you have your property defined that way, you should have dealloc method as:
- (void) dealloc{
[myView release];
[super dealloc];
}
So, the answer is NO you should not release it but actually is not correct.
Please take a look:
myView = [[UIView alloc] initWithFrame:myFrame]; //myView retainCount is 1
[self.view addSubview:myView]; //retainCount is 2
[myView release]; //retainCount is 1 again
later in dealloc method
- (void) dealloc{
[myView release]; // retainCount becomes 0, is deallocated
[super dealloc]; // subviews of self.view are released but myView was already deallocated!, so you have over released myView once ;(
}
This is the correct way: (Use your properties ;) )
UIView *aView = [[UIView alloc] initWithFrame:myFrame]; // init, retainCount is 1
self.myView = aView; // retainCount becomes 2
[aView release]; // retainCount becomes 1 again and we are fine.
[self.view addSubview:self.myView]; //retainCounts becomes 2 again.
even if it is 2 there is no problem because when self.view is deallocated its subviews also will be released. Hence self.myView retainCount will become 1 again later when self is deallocated.
- (void) dealloc{
[myView release]; //retainCounts becomes 1
[super dealloc]; // all its subviews of self.view are released hence myView retaincount becomes 1 and is released corretly
}
What is the difference?
Suppose self.myView is also retained by other object X and with the former approach, X's view will be pointing to an invalid object, because it was already released.
Hope it helps
EDIT2
As bbum's indication, this is a mini-mini-short tutorial on properties:
when you have
#property (... retain) NSObject *retainVar;
#property (... assign) NSObject *assignVar;
#property (... copy) NSObject *copyVar;
and you #synthesize them
is like having the following setters:
// retain
-(void)setRetainVar:(NSObject *)var {
if (retainVar != var) {
[retainVar release];
retainVar = [var retain];
}
}
//assign
-(void)setAssignVar:(NSObject *)var {
assignVar = var;
}
//copy
-(void)setCopyVar:(NSObject *)var {
if (copyVar != var) {
[copyVar release];
copyVar = [var copy];
}
}
(this means that if you assign directly an object you have to make sure is something equivalent to above setters, from the memory management point of view)
and your dealloc method should be something like:
- (void) dealloc{
[retainVar release];
assignVar = nil;
[copyVar release];
[super dealloc];
}
When setting your ivars
for example, inside of init:
- (id) init{
if ((self = [super init])){
//this is ok
retainVar = [[NSObject alloc] init];//but is retainVar was not nil we will have a leak ;(
//This is better
NSObject *obj = [NSObject alloc] init];
self.retainVar = obj;
[obj release];
//this is BAD
assignVar = [[NSObject alloc] init];//because this is like retaining it, later it will leak
//below is correct
NSObject *obj = [[[NSObject alloc] init] autorelease];
assignVar = obj;
//copy is pretty much like retain,
//this is ok
copyVar = [[NSObject alloc] init]; //but, if copyVar was not nil is a leak!
//below is better
NSObject *obj = [NSObject alloc] init]:
self.retainVar = obj;
[obj release];
}
return self;
}
Apple's "Learning Objective C - A Primer" tells you about that and more:
http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/