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
Related
I have a base class who's constructor sets the objects state given some external data:
#implementation XMlContent
- (id)initWithParser:(NSXMLParser *)xmlParser {
self = [super init];
if (self) {
if(xmlParser != nil){
_documentFormat = #"xml";
_xmlParser = xmlParser;
xmlParser.delegate = self;
} else{
return nil;
}
}
return self; }
I have a subclass of this object and I have overridden this constructor like so (probably wrong):
#implementation AtomContent
- (id)initWithParser:(NSXMLParser *)xmlParser
{
self = [super init];
if (self) {
if(xmlParser != nil){
self.xmlParser = xmlParser;
self.xmlParser.delegate = self;
self.documentFormat = #"atom";
} else{
return nil;
}
}
return self;
}
The issue I have is that when creating an instance of the subclass (AtomContent in this case) I get back an instance of the superclass and thus superclass methods are called rather than those in the subclass. Looking at my subclass constructor its obvious why. My question is how do I property implement the subclass constructor so the super class is init'd but references to 'self' are to the subclass? Should I not override the constructor but rather use setters to set object state?
Corrections
I get back an instance of the superclass and thus superclass
-No. Specifically my issue is that base class implementation is used (the NSXMLParser delegate methods) rather than the subclass'. I am, indeed, returned an instance of the subclass.
constructor
-Yeah ok.. "Initializer"
Thanks!
You're not calling the designated initializer from your subclass initializer, which causes you to need to repeat a lot of work in the subclass's initWithXMLParser: implementation. Here's how I'd do it:
// Superclass
- (id)initWithParser:(NSXMLParser *)xmlParser {
self = [super init];
if (self) {
if(xmlParser != nil) {
_documentFormat = #"xml";
_xmlParser = xmlParser;
xmlParser.delegate = self;
}
else {
return nil;
}
}
return self;
}
// Subclass
- (id)initWithParser:(NSXMLParser *)xmlParser
{
self = [super initWithParser:xmlParser];
if (self) {
self.documentFormat = #"atom";
}
return self;
}
I should add that if you are truly getting an instance of the superclass returned, then your problem would be in allocation, not initialization, as the isa pointer and hence the method lookup tables are set inside alloc and thus are set at the time init is called. I would rate the chance of that being your problem in actuality as being low.
i am working on a project in wich i want to use Singleton Pattern model.
i want to any data model of my this project fallow Singleton Pattern.
i study the apple documentation regarding this
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaDesignPatterns/CocoaDesignPatterns.html#//apple_ref/doc/uid/TP40002974-CH6-SW6
and
http://www.oodesign.com/singleton-pattern.html
http://www.daveoncode.com/2011/12/19/fundamental-ios-design-patterns-sharedinstance-singleton-objective-c/
now i know my custom object classes should fallow the main rule of allocing a object but the i need the complete implementation like using of this class object
i am new in iphone app development so if i am wrong in any place in this Question please guide
Try this:
#implementation Singleton
+ (Singleton *)sharedInstance
{
static Singleton *obj = nil;
if (obj == nil)
obj = [[self alloc] init];
return obj;
}
#end
static MyClass *_sharedInstance;
+ (MyClass *)sharedMyClass
{
#synchronized([MyClass class]) {
if (_sharedInstance == nil)
[[self alloc] init];
return _sharedInstance;
}
return nil;
}
+(id) alloc
{
#synchronized([MyClass class]) {
NSAssert(_sharedInstance == nil, #"Attempted to allocate a second instance of MyClass.");
_sharedInstance = [super alloc];
return _sharedInstance;
}
return nil;
}
+ (id) allocWithZone:(NSZone *)zone
{
#synchronized([MyClass class]) {
NSAssert(_sharedInstance == nil, #"Attempted to allocate a second instance of MyClass.");
_sharedInstance= [super allocWithZone:zone];
return _sharedInstance;
}
return nil; //on subsequent allocation attempts return nil
}
- (id) copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return NSUIntegerMax;
}
- (oneway void)release
{
// Do nothing
}
- (id)autorelease
{
return self;
}
If you can target iOS 4 or above, I will take the following way:
//.h
+(MySingletonClass *)mySharedInstance;
-(void)doSomething;
//.m
+(MySingletonClass *)mySharedInstance {
static dispatch_once_t pred;
static MySingletonClass *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingletonClass alloc] init];
});
return shared;
}
-(void)doSomething
{
}
// override also the init if you want
To access it, do an #import MySingletonClass.h and use it wherever you want like the following:
MySingletonClass* mySharedInstance = [MySingletonClass mySharedInstance];
[mySharedInstance doSomething];
I want to any data model of my this project fallow Singleton Pattern.
Based on my experience, I would not abuse on singletons. The application could become difficult to maintain. To avoid this, put the data models within your singleton. You can access data model directly (creating properties around them) or using public methods (like for example doSomething) as wrappers.
Hope this helps.
This might be a useful reference: http://cocoasamurai.blogspot.com/2011/04/singletons-your-doing-them-wrong.html
Typically I create the object in the
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
method and have it live in that object. I make it available to the rest of the app with a macro:
#define APPLICATION ((AppDelegate*)([UIApplication sharedApplication].delegate))
as a readonly property of the app delegate
The benefit of this type of approach if you are in to mocking is that it is just another object property rather than a hidden static object.
I use:
#import <Foundation/Foundation.h>
#interface iCode_Framework : NSObject
#property (readonly, nonatomic) unsigned int iBufCapacity;
#property (readonly, nonatomic) unsigned int iPort;
#property (readonly, nonatomic) NSString * urlStr;
#end
#import "iCode_Framework.h"
static iCode_Framework * instance;
#implementation iCode_Framework
#dynamic iBufCapacity;
#dynamic iPort;
#dynamic urlStr;
- (unsigned int)iBufCapacity
{
return 1024u;
};
- (unsigned int)iPort
{
return 1978u;
};
- (NSString *)urlStr
{
return #"localhost";
};
+ (void)initialize
{
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
+ (id)allocWithZone:(NSZone * const)notUsed
{
return instance;
}
#end
Which is used exactly like a normal class, you call alloc and init! It is often convenient to assign to a variable to give a shorthand, because alloc and init are long to type, e.g.:
#import "iCode_FrameworkTests.h"
#import "iCode_Framework.h"
static iCode_Framework * c;
#implementation iCode_FrameworkTests
+ (void)initialize
{
c = [[iCode_Framework alloc] init];
}
- (void)setUp
{
[super setUp];
// Set-up code here.
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testSingletonNotNil
{
STAssertNotNil(c, nil);
}
- (void)testSingletonProperty
{
STAssertEqualObjects(c, [iCode_Framework alloc], nil);
}
- (void)testIBufCapacity
{
STAssertEquals(c.iBufCapacity, 1024u, nil);
}
#end
The advantage of this approach is it is used exactly like any other class and can therefore be mocked for testing.
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]) {
//}
I have created my iPhone apps but I have a problem.
I have a classViewController where I have implemented my program.
I must alloc 3 NSMutableArray but I don't want do it in grapich methods.
There isn't a constructor like Java for my class?
// I want put it in a method like constructor java
arrayPosition = [[NSMutableArray alloc] init];
currentPositionName = [NSString stringWithFormat:#"noPosition"];
Yes, there is an initializer. It's called -init, and it goes a little something like this:
- (id) init {
self = [super init];
if (self != nil) {
// initializations go here.
}
return self;
}
Edit: Don't forget -dealloc, tho'.
- (void)dealloc {
// release owned objects here
[super dealloc]; // pretty important.
}
As a side note, using native language in code is generally a bad move, you usually want to stick to English, particularly when asking for help online and the like.
/****************************************************************/
- (id) init
{
self = [super init];
if (self) {
// All initializations you need
}
return self;
}
/******************** Another Constructor ********************************************/
- (id) initWithName: (NSString*) Name
{
self = [super init];
if (self) {
// All initializations, for example:
_Name = Name;
}
return self;
}
/*************************** Another Constructor *************************************/
- (id) initWithName:(NSString*) Name AndAge: (int) Age
{
self = [super init];
if (self) {
// All initializations, for example:
_Name = Name;
_Age = Age;
}
return self;
}
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];
}