Determine class in init method - objective-c

I have a global dictionary with Class objects and NSString keys. Every custom subclass of my Property class can register itself on the Property superclass using
+ (void)registerPropertyClass:(Class)pclass forNamePrefix:(NSString *)namePrefix
A Property is initialized with a name and value. What I want to do now is return a different class from the init method based on the name prefix (if a registered class matches).
Would something like this be correct?
- (id)initWithName:(NSString *)name value:(NSString *)value
id instance = self;
NSArray *registeredPrefixes = [kCKPropertyClasses allKeys];
for (NSString *prefix in registeredPrefixes) {
if ([name rangeOfString:prefix].location == 0) {
instance = [[kCKPropertyClasses objectForKey:prefix] alloc];
break;
}
}
self = [instance init];
if (self) {
self.name = name;
self.value = value;
}
return self;
}
UPDATE: Forgot to mention that this project is using ARC (so no retain/release)

Without compiling it, that'll work, but leak under manual-retain-release. You need to release self prior to the reassignment self = [instance init];.
With ARC, it should be fine.

Related

Why can a factory method call an instance method?

As we all know, factory methods can't call instance methods. Why does the code below work?
// .m file implementation DemoClass
// custom instance init method
- (instancetype)initWithDate:(NSDate *)date {
if (self = [super init]) {
self.lastTime = date;
}
return self;
}
// custom factory method
+ (instancetype)DemoClassWithDate:(NSDate *)date
//here calling instance method initWithDate:
return [[self alloc] initWithDate:date];
}
[self alloc] will return an instance. initWithDate is just an instance method. No reason why a class method wouldn't be allowed to call an instance method on an instance.
PS. I would highly recommend that you check your compiler settings and tell the compiler to give you a warning if the result of '=' is used as a boolean value. This will prevent many hard to find bugs. You'll have to change the if to
if ((self = [super init]) != nil)
Because it has a reference to the, newly created, instance:
return [[self alloc] initWithDate:date];
// ^^^^^^^^^^^^
// reference

Objective-C return nil inside init

I have a model for example Product. Product has required property id.
So I thought that model can't be without property.
My implementation of init method for Product class.
- (instancetype)initWithProductId:(NSNumber *)productId {
if ([productId integerValue] <= 0) {
return nil;
}
self = [super init];
if (self) {
_productId = productId;
}
return self;
}
Is it legal to return nil inside initilization methods? Will I get memory management problems?
And could some one explain why it's legal and when I should use it.
Almost. Obviously someone has called alloc to create an unitialised object, and is now calling your init method. With your code, the super class init will not be called. When the object is deallocated, the super class dealloc is called. But that might fail if superclass init was never called, depending on the implementation of your superclass. So the correct way would be:
if ((self = [super init]) != nil) {
if (productId.integerValue <= 0)
return nil;
_productId = productId;
}
return self;

Why my own Object not populate NSMutableArray property?

Hi i want to implement my own Objects to manage my data, i was trying to make a two classes.
Class Continents that contains a Continent Objects
Here is my implementation:
#implementation OsContinents
#synthesize continentes;
-(id)init{
return [super init];
}
-(NSUInteger)count{
NSLog(#"%u",[continentes count]);
return [continentes count];
}
-(void)add:(OsContinent *)continente{
[continentes addObject:continente];
}
-(OsContinent *)getElementByIndex:(NSUInteger)index{
return [continentes objectAtIndex:index];
}
-(void)deleteContinentByIndex:(NSUInteger)index{
return [continentes removeObjectAtIndex:index];
}
-(void)deleteContinent:(OsContinent *)objContinent{
return [continentes removeObject:objContinent];
}
-(NSMutableArray *)getAll{
return continentes;
}
#end
Next i want to populate *continents Property with "Continent" Objects like this.
OsContinents *continentesCollection = [[OsContinents alloc] init];
for (NSString *strContinente in [data allKeys]) {
OsContinent *con = [[OsContinent alloc] init];
[con setContinente:strContinente];
NSLog(#"%#",[con getContinente]);
[continentesCollection add:con];
}
NSLog(#"%u",[continentesCollection count]);
But allways got ZERO in de count Method.
Note: NSLog(#"%#",[con getContinente]) print de data OK, the Continent Object is OK, the problem is in the "*continentes" inside the Continents Object-
Any Clue?
Your initializer does nothing but initialize the superclass. Use it to set up your own class:
- (id)init
{
self = [super init];
if (self)
{
_continentes = [[NSMutableArray alloc] init];
}
return self;
}
Otherwise, continentes will remain nil. Messaging nil is valid: methods simply don't do anything, and return 0.
If you want to completely hide the underlying mutable array (which is perfectly fine) then don't advertise it in your .h file as a property. Instead, at the beginning of your #implementation, declare a semi-private instance variable:
#implementation OsContinents
{
NSMutableArray *_continentes;
}
I say "semi-private" because you can always use the runtime engine to introspect objects. But it'll be hidden from normal use. If you ever subclass your object, you can always move the instance variable declaration from your #implementation to your #interface so that subclasses can get at it.

Objective-C, class identity

I have the following situation, i can't resolve:
#interface Deck : NSObject
#interface MasterDeck : Deck
#interface PlayerDeck : Deck
Inside MasterDeck class, as part of initialization, i call
[self cutDeckImageIntoCards]; // We don't get to execute this method
Call results in an error [PlayerDeck cutDeckImageIntoCards]: unrecognized selector sent to instance
Indeed, PlayerDeck does not have this method .. but why is it being called at all?
After looking at MasterDeck's initialization i added a few debugging statements:
static MasterDeck *gInstance = NULL;
+(MasterDeck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
// MasterDeck
self = [super init];
// PlayerDeck
if (self) {
// Lots of stuff
[self cutDeckImageIntoCards]
// Some more stuff
}
gInstance = self;
return gInstance;
}
Ok, so MasterDeck is PlayerDeck because' Deck thinks it is a PlayerDeck ... Deck confirms
Deck is created as follows:
static Deck *gInstance = NULL;
+(Deck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
self = [super init];
if (self) {
// Do something
}
NSLog(#"Deck thinks it's a %#", [[self class ]description]); // PlayerDeck
gInstance = self;
return gInstance;
}
So, again
#interface Deck : NSObject
Assuming above Singleton Implementation, why would Deck think it's actually a PlayerDeck?
So the way you've written this, if you create the PlayDeck instance first, then the Deck instance is now a PlayDeck.
And then if you go to create the MasterDeck instance, your call to [super init] dutifully returns that previous PlayDeck instance.
So why is Deck a singleton at all? Deck has two subclasses that are singletons, but are you really looking for a singleton Deck also?
At a minimum, you can make this sort of work by not setting gInstance from within each init. Let the class method do that. Just return self from each of the init's. Also, remove the check for gInstance being not null, other Deck's init will always return Deck's instance once you have an instance of Deck.
But beyond that, I would rethink this idea a bit. Hope that helps.
You'll probably want to separate your singleton class from the actual class.
Try implementing it as in this example,
+(id) instance {
static dispatch_once_t pred;
static MasterDeck *sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[MasterDeck alloc] init];
});
return sharedInstance;
}
What happens if you replace [[self alloc] init] with [[MasterDeck alloc] init]?
It may be that somehow self is PlayerDeck. To make sure, you could NSLog([self description]) just before calling + alloc.
Edit
I assume that the interesting part of the code you have above is part of the #implementation of MasterDeck. My suggestion would be to try a lot more logging, including determining what super and [self class] are before calling [super init], although these may be misleading...
Also, as a side note, I believe that you should call [self release] in init if you are returning the previously-created instance.
What does the [super init] method look like? Can you step into it, or is it the default initializer?
Edit 2
I think you're doing singletons wrong. If you initialize a PlayerDeck, that would create a singleton in Deck which is an instance of PlayerDeck. Then later, when you initialize a MasterDeck, calling [super init] will return the instance already created by the PlayerDeck.
It looks like you try to be clever, but fact is - often the computer is even smarter. :)
Your deck class caches an instance in gInstance - in fact, it looks like it may store a Deck, a PlayerDeck, or a MasterDeck, depending on what and how you call / instantiate first. After that, this very instance is returned by that init method.
I strongly suggest to get this code clean and readable. I bet there are numerous problems with this code - but your problem is already a good example. Your logic (which should be simple, I guess) can surely be implemented much easier.
Note - I'm not against singletons, but this sort of code stacking is an absolute no-go. It's hard to get more dependency logic into those lines. ;)

How to write an Objective-C convenience constructor

I'm trying to add a convenience constructor to my custom object.
Similar to [NSArray arrayWithArray:]
I know it involves a class method that returns an auto released object. I've been googling around but all I can seem to find is the definition of a convenience constructor but not how to write one.
Let's say you have the following:
#class PotatoPeeler : NSObject
- (instancetype)initWithWidget: (Widget *)w;
#end
Then to add a factory method, you'd change it to this:
#class PotatoPeeler : NSObject
+ (instancetype)potatoPeelerWithWidget: (Widget *)w;
- (instancetype)initWithWidget: (Widget *)w;
#end
And your implementation would simply be:
+ (instancetype)potatoPeelerWithWidget: (Widget *)w {
return [[[self alloc] initWithWidget: w] autorelease];
}
Edit: replaced id with instancetype. They are functionally identical, but the latter provides better hints to the compiler about the method's return type.
Generally my approach is the following: first I create a normal initializer method (instance method), then I create a class method that calls the normal initializer. It seems to me Apple uses the same approach most of the time. An example:
#implementation SomeObject
#synthesize string = _string; // assuming there's an 'string' property in the header
- (id)initWithString:(NSString *)string
{
self = [super init];
if (self)
{
self.string = string;
}
return self;
}
+ (SomeObject *)someObjectWithString:(NSString *)string
{
return [[[SomeObject alloc] initWithString:string] autorelease];
}
- (void)dealloc
{
self.string = nil;
[super dealloc];
}
#end