When to release a class with delegates - iphone

A quick question to delegates. Lets say, CLASSA has a delegate defined:
#protocol MyDelegate
-(void) didFinishUploading;
#end
In CLASSB I create an instance of CLASS A
-(void) doPost {
CLASSA *uploader = [[CLASSA alloc] init];
uploader.delegate = self; // this means CLASSB has to implement the delegate
uploader.post;
}
and also in CLASSB:
-(void)didFinishUploding {
}
So when do I have to release the uploader? Because when I release it in doPost, it is not valid anymore in didFinishUploading.
Thanks

Release it in didFinishUploding. Put CLASSA * uploader in the instance variables of CLASSB to allow for that.

Instead of creating CLASSA instance in doPost method.
It is better to create CLASSA *uploader = [[CLASSA alloc] init]; in the init method and release uploader in dealloc.
make uploader as member variable.
-(id) init
{
self = [super init];
if(self)
{
uploader = [[CLASSA alloc] init];
uploader.delegate = self;
}
retrurn self;
}
-(void) doPost
{
uploader.post;
}
-(void)didFinishUploding
{
uploader.delegate = nil;
//your code
}
-(void) dealloc
{
[uploader release];
[super dealloc];
}

Related

Global instance of class

Im trying to make an instance of a class available in all methods in the .m file. Instead of doing this inside every method:
-(void)viewDidLoad
{
Class *c = [Class new];
}
My attempt in the .h file:
#interface RandomViewController : UIViewController
{
Class *c [Class new];
}
But It does'nt work, I get error
Expected ';' at end of declaration list
How would I make this possible?
Thanks
Create an instance variable, and set it up in your designated initializer. Here's a sample #implementation block.
#implementation RandomViewController {
// Instance variable names should start with an underscore, by convention.
Class *_c;
}
...
// Designated initializer for UIViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_c = [[Class alloc] init];
}
return self;
}
...
- (void)someMethodThatNeedsAClassInstance
{
[_c doSomething];
}
If you're not using ARC, you'll need to add [_c release] to your dealloc implementation.
Now, in every method in your class, you may refer to _c and get the instance you created in the initializer. As a side note, this isn't really a 'global' instance - it is per-object. Each individual view controller you create will have its own instance of _c. Also, an even better approach is to declare a property and not bother declaring an instance variable at all.
In .h file:
#interface RandomViewController : UIViewController
{
Class *c;
}
In .m file:
-(void)viewDidLoad
{
c = [[Class alloc] init];
}
what do it do the class method? if return void:
Class.h file:
#interface Class: NSObject
{
}
- (void)new;
RandomViewController.h
#interface RandomViewController : UIViewController
{
Class *c;
}
RandomViewController.m file
-(void)viewDidLoad
{
c = [[Class alloc] init];
//call the method
[c new];
}

How to copy superclass properties to a subclass?

Let's say I have two Classes like so:
Car
{
NSInteger wheels;
NSInteger bumpers;
}
+ (Car *)carWithData:(NSDictionary *)carData;
Lexus : Car
{
GPS *navigation;
}
+ (Lexus *)carWithData:(NSDictionary *)carData;
carWithData: is a simple helper method that creates an instance of Car populated with variables from carData. Lexus' version would also set the navigation data.
How would Lexus' carWithData look like without duplicating code from Car?
This is accomplished by calling super's implementation of init… in the init method:
//Car.m:
- (id)initWithData:(NSDictionary *)carData {
self = [super init];
if (self) {
//setup generic car properties:
self.wheels = [carData objectForKey:#"wheels"]; //example
self.bumpers = [carData objectForKey:#"bumpers"]; //example
}
return self;
}
+ (id)carWithData:(NSDictionary *)carData {
return [[[self alloc] initWithData:carData] autorelease];
}
//Lexus.m:
- (id)initWithData:(NSDictionary *)carData {
//this call to super is where the car's generic properties get initialized:
self = [super initWithWithData:carData];
if (self) {
//setup lexus car properties:
self.navigation = [carData objectForKey:#"navigation"]; //example
}
return self;
}
//there is no need to override super's [carWithData:] method as it's only a wrapper anyway.
Also note that both the initWith… and carWith… methods return id, not Car or Lexus.
The way your code is set up you end up with casting problems, where [Lexus carWithData:dataDict] does return an object of class Lexus, but the compiler doesn't know about it, as it expects a Car.
You would not define the methods with different signatures like:
+ (Car *)carWithData:(NSDictionary *)carData;
+ (Lexus *)carWithData:(NSDictionary *)carData;
you should instead use
+ (id)carWithData:(NSDictionary *)carData;
The implementation of the subclass would then look like
- (id)initWithData:(NSDictionary *)carData;
{
self = [super initWithData:carData];
if (self) {
_navigation = [carData valueForKey:#"navigation"];
}
return self;
}
+ (id)carWithData:(NSDictionary *)carData;
{
return [[[self alloc] initWithCarData:carData] autorelease];
}
Here would be my solution:
// interface
-(id) initWithCarData:(NSDictionary *) carData;
+(Car *) carWithCarData:(NSDictionary *) carData;
// car implementation
-(id) initWithCarData:(NSDictionary *) carData
{
if (self = [super init])
{
// initialize car data
}
return self;
}
+(Car *) carWithCarData:(NSDictionary *) carData
{
// note that 'self' here is the current class,
// there is no need to overwrite this method in the subclass
return [[self alloc] initWithCarData:carData];
}
// lexus implementation
-(id) initWithCarData:(NSDictionary *) carData
{
// initialize the variables that the superclass recognizes
if (self = [super initWithCarData:carData])
{
// initialize the lexus data
}
return self;
}
So, when you call [Lexus carWithCarData:myData] it ends up calling the Lexus's init method, not the Car's.
There's another way, which is more generic, by using the NSCoding protocol and NSKeyedArchiver.
If the object you want to copy into a subclass of yours implements the NSCoding protocol, which is the case for many NS... based classes, the following totally legal and safe trick can be used:
// Assumptions:
// copyFrom is an object of ClassA
// We have a ClassB that is a subclass of ClassA
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[copyFrom encodeWithCoder:arch]; // this archives its properties
[arch finishEncoding];
NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
ClassB *ourCopy = [[ClassB alloc] initWithCoder:ua]; // this restores the properties into our new object

NSXMLParser not adding objects

I'm new to the NSXMLParser, so I decided to take this tutorial: http://www.xcode-tutorials.com/parsing-xml-files/ and it was great. But in my app I used parser not in AppDelegate, but in other ViewController. I changed initialization to this:
-(XMLParser *)initXMLParser {
[super init];
viewController = (ViewController *)[[UIApplication sharedApplication] delegate];
return self;
}
The parser itself works well but it doesn't add any objects to the array in view controller in which the objects should be added. Can anyone help?
Your code call still your app delegate
Try with this kind of code
#interface XMLParser : NSObject {
NSMutableString *currentElementValue;
id<NSXMLPArserDelegate> delegate;
Book *aBook;
}
-(XMLParser *)initXMLParserWithDelegate:(id<NSXMLPArserDelegate>) delegate;
#end;
-(XMLParser *)initXMLParserWithDelegate:(id<NSXMLPArserDelegate>) _delegate {
self = [super init];
if(self) {
delegate = _delegate;
}
return self;
}
And set the delegate with self in your constructor

singleton class in objective-C

I want to have one object that is initialized in the delegate and I want to be able to use this object anywhere across view controllers (doesn't depend on what view I am currently at). I am guessing the solution to this would be to have a singleton class, so far I have the following:
#interface LocationManager : NSObject <CLLocationManagerDelegate>{
NSDate *enter;
NSDate *exit;
CLLocationManager * manager;
}
#property (nonatomic, retain) NSDate * enter;
#property (nonatomic, retain) NSDate * exit;
- (BOOL)registerRegionWithLatitude:(double)latitude andLongitude:(double)longitude;
+ (LocationManager *)instance;
#end
#import "LocationManager.h"
#implementation LocationManager
#synthesize enter;
#synthesize exit;
#pragma mark - CLLocationManager delegate
static LocationManager *gInstance = NULL;
+ (LocationManager *)instance
{
#synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
#end
Is this correct? So all I need to do to access this is just to call instance? Inside LocationManager I also want to have only one CLLocationManager, called manager.. however, where do I initialize it so I only have one? Can I do the following? Most other singleton examples doesn't have any variables in the class, so that's where I got confused
+ (LocationManager *)sharedLocationManager
{
#synchronized(self)
{
if (lm == NULL){
lm = [[self alloc] init];
lm.manager = [[CLLocationManager alloc] init];
lm.manager.delegate = lm;
}
}
return(lm);
}
Basically -- yes.
Just a couple of small things:
static LocationManager *gInstance = NULL;
instead of NULL, you should use nil, it's a convention in Objective-C.
You should also overwrite alloc, new, copyWithZone:, and mutableCopyWithZone:. From Buck/Yacktman: "Cocoa Design Patterns", p. 153:
+ (id)hiddenAlloc
{
return [super alloc];
}
+ (id)new
{
return [self alloc];
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
[self retain];
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [self copyWithZone:zone];
}
This way, your singleton object cannot be copied. You need to call hiddenAlloc from your instance method (by the way, the method to access a Singleton object is often called sharedInstance in Objective-C).
For other singleton styles with their pros and cons, check out this question.
Personally, I prefer this style (copied from one of the answers on that link):
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
In fact, there's a tried-and-true method to create singletons already. Download the SynthesizeSingleton.h file (from a Cocoa with Love article). It contains a massive amount of pre-processor code which will generate any singleton for you. Hit the article for more details.
Since the factory method "instance" is a class-level method, the #synchronized block should be
#synchronized([LocationManager class]) {
//}

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

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.