How to override init method to return other class? - objective-c

How to override init method to return other class? Like this
ClassA.m
- (id)initWithType:(int)type{
if(type == 0){
return [[ClassB alloc] init];
}
else if(type == 1){
return [[ClassC alloc] init];
}
return [self init];
}
id classInstance = [[ClassA alloc] initWithType:someType];
So, in ARC, if I should care about the alloc operation of ClassA?
And, in NonARC, what's the difference?
BTW, Using class method to implement is not in discussion, I only care about how to override init method to return other class?

your code looks fine with ARC. for non-ARC, you need to release self
- (id)initWithType:(int)type{
if(type == 0){
[self release]; self = nil;
return [[ClassB alloc] init];
}
else if(type == 1){
[self release]; self = nil;
return [[ClassC alloc] init];
}
return [self init];
}

Related

non-ARC: Should I call [super init] before [self release]?

I have a custom non-ARC init, and I wonder if I should call [super init] before releasing self.
Solution A, not calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
if (anInteger == 0)
{
[self release];
return nil;
}
self = [super init];
if (!self)
return nil;
// Initialization code
self.i = anInteger;
return self;
}
Solution B, calling [super init] before [self release]:
- (instancetype)initWithInteger:(NSInteger)anInteger
{
self = [super init];
if (!self)
return nil;
if (anInteger == 0)
{
[self release];
return nil;
}
// Initialization code
self.i = anInteger;
return self;
}
I would go with the second pattern. The reason for this is that super's dealloc might possibly rely on something in super's init having been done to work properly.
Here is a (very) contrived example, this is the init and dealloc method in a class that you are subclassing.
#implementation ASuperClass
{
char* foo;
}
-(id) init
{
self = [super init];
if (self != nil)
{
foo = strdup("blah blah blah");
}
return self;
}
-(void) dealloc
{
if (foo[1] == 'l')
{
NSLog(#"Second character was l");
}
free(foo);
}
In the above, if this is the class you inherited from, your first pattern will throw an EXC_BAD_ACCESS on a null pointer dereference.

Can i achieve the real singleton in objective-c like java c#

init method is declared in NSObject class hence, the client code can create a new instance of my singleton class, is there any way to achieve the real singleton such that client cannot create a new instance.
Just do this:
static SingletonClass *singleton;
+ (SingletonClass *)sharedInstance
{
#synchronized(self) { //For thread safety
if (singleton == nil) {
[[self alloc] init];
}
return singleton;
}
}
-(id)init
{
if (singleton) { //This way init will always return the same instance
return singleton;
}
self = [super init];
if (self) {
singleton = self;
}
return singleton;
}
This is a proper way to do a singleton in objective c
+ (id)sharedManager
{
static dispatch_once_t onceQueue;
static SingletonObjectClass *singleton = nil;
dispatch_once(&onceQueue, ^{
singleton = [[self alloc] init];
});
return singleton;
}
- (id)init {
self = [super init];
if (self) {
//.....
}
return self;
}
init method is for initialization of instance variables. on its own will not create the object. Alloc, copy methods needs to be overridden to achieve a real single ton.
Hope this should clarify.
+ (id)alloc {
NSLog(#"%#: use +sharedInstance instead of +alloc", [[self class] name]);
return nil;
}
+ (id)new {
return [self alloc];
}
+ (SingletonClass *)sharedInstance {
static SingletonClass *myInstance = nil;
if (!myInstance)
{
myInstance = [[super alloc] init];
}
return myInstance;
}
You can return a static object of the class each time, making it a singleton.
#implementation Singleton
#synthesize testVar;
+ (Singleton*) sharedObject {
static Singleton * myInstance = nil;
if (myInstance == nil) {
myInstance = [[[self class] alloc] init];
testVar = 5;
// Set default values if needed
return myInstance;
}
To access object and its members:
[[Singleton sharedObject] testVar]

Objective-C calling instance method within Singleton

I created a Singleton class named DataManager. I've set up some caching. However, when attempting to call the cache instance methods from within another instance method of DataManager, I'm getting EXC_BAD_ACCESS error on the line:
NSMutableArray *stops = [[DataManager sharedInstance] getCacheForKey:#"stops"];
DataManager.h
#interface DataManager : NSObject {
FMDatabase *db;
NSMutableDictionary *cache;
}
+ (DataManager *)sharedInstance;
// ... more methods
- (id)getCacheForKey:(NSString *)key;
- (void)setCacheForKey:(NSString *)key withValue:(id)value;
- (void)clearCache;
DataManager.m
- (NSArray *)getStops:(NSInteger)archived {
NSMutableArray *stops = [[DataManager sharedInstance] getCacheForKey:#"stops"];
if (stops != nil) {
NSLog(#"Stops: %#", stops);
return stops;
}
stops = [NSMutableArray array];
// set stops...
[[DataManager sharedInstance] setCacheForKey:#"stops" withValue:stops];
return stops;
}
It seems to occur when called from another view controller. That is the first view controller no error, second view controller, error.
This is my first attempt at a Singleton, so I'm sure I am making a simple mistake. But I am failing to see it myself.
Note: I've tried [self getCache...] with the same result.
UPDATE
Here is my Singleton implementation. Adapted from http://www.galloway.me.uk/tutorials/singleton-classes/
+ (DataManager *)sharedInstance {
#synchronized(self) {
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
return instance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedInstance] 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]) {
if (db == nil){
BourbonAppDelegate *appDelegate = (BourbonAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate createEditableCopyOfDatabaseIfNeeded];
db = [[FMDatabase alloc] initWithPath:[appDelegate getDBPath]];
}
if (![db open]) {
NSAssert(0, #"Failed to open database.");
[db release];
return nil;
}
[db setTraceExecution:YES];
[db setLogsErrors:TRUE];
cache = [NSMutableDictionary dictionary];
NSLog(#"cache: %#", cache);
}
return self;
}
Your cache object is autoreleased, so it's no longer in memory when you try to access it.
Use [NSMutableDictionary alloc] init] instead of [NSMutableDictionary dictionary] to get an instance that is retained.
Not a direct answer to your question, which was already answered.
However I'd just like to point out that your singleton implementation is sub-optimal. #synchronized is very expensive, and you can avoid using it every time you access the singleton:
if (!instance) {
#synchronized(self) {
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
}
An even better way to initialize a singleton would be:
+ (DataManager *)sharedInstance {
static DataManager *instance;
static dispatch_once_t donce;
dispatch_once(&donce, ^{
instance = [[self alloc] init];
});
return instance;
}

Confuse in one objective -c Singleton example

I have been reading about an objective-c singleton example from http://getsetgames.com/2009/08/30/the-objective-c-singleton/.
The .m code looks like
+(MySingleton*)sharedMySingleton{
#synchronized([MySingleton class])
{if (!_sharedMySingleton)
if(!_sharedMySingleton)
return _shareMySingleton;
}
....
-(id)init{
self = [super init];
if(self != nil){}
return self;
}
....
Thanks for your example, but I have a confused place, in your code, I am wondering in which place the static MySingleton* _sharedMySingleton is initialized. for example if we would have to have some implementation like
-(id)init{
self = [super init];
if(self != nil){
_sharedMySingleton = self
}
return self;
}
You miscopied a part of the code from the link you posted by mistake.
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([MySingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
-(void)sayHello {
NSLog(#"Hello World!");
}
#end
looking at the code from the post you are asking about it actually makes sense.
What he is doing in the static method +(MySingleton*)sharedMySingleton is that he is checking if the _sharedMySingletonobject has a value he is returning it, if not it gets initialized.
The alloc method is the one setting the singleton object, its not being set in the initializer. technically its the same, since its going to be point on the same object that will be initialized a moment after.
I hope that clarifies your confusion.
you can use macro from http://code.google.com/p/google-toolbox-for-mac/source/browse/trunk/Foundation/GTMObjectSingleton.h it is vvveeerryyy easy to use
in implementation file (m)
GTMOBJECT_SINGLETON_BOILERPLATE(ClassName, sharedInstance)
and header file
+ (ClassName *) sharedInstance;

Potential Leak of an object allocated

+ (WAController*) sharedWAController {
#synchronized([WAController class]) {
if (!_sharedWAController)
[[self alloc] init];
return _sharedWAController;
}
}
This is show in potential leak
for [[self alloc] init];
Why its leak ?
You never assign it to anything so it will just allocate a new object and leak it. To fix the leak and the incorrectly working code assign _sharedWAController
if (!_sharedWAController)
_sharedWAController = [[self alloc] init];
make it : _sharedWAController = [[self alloc] init];
+ (WAController*) sharedWAController {
#synchronized([WAController class]) {
if (!_sharedWAController)
{
_sharedWAController = [[self alloc] init];
}
return _sharedWAController;
}
}
+ (CommonUtility*) sharedUtility {
#synchronized([CommonUtility class]) {
if (!_sharedUtility)
_sharedUtility = [[self alloc] init];
return _sharedUtility;
}
}
+ (id) alloc {
#synchronized([CommonUtility class]) {
_sharedUtility = [super alloc];
return _sharedUtility;
}
}
I try like this is this perfect. But I allocated _sharedUtility object where i need to release. this In dealloc or autorelesae it.