I've got two classes: Alpha and Beta.
Beta inherits from Alpha.
How do I write a method in Alpha such that if I call it with an object of class Beta, it will return a new Beta object?
For example:
- (Alpha *)makeAnother {
return [[[self class] alloc] init] autorelease];
}
I thought this would work, but it doesn't.
Change the return type from Alpha* to id in this case. The rest of it is right.
Not sure what you're trying to do here, when you use polymorphism the ultimate goal is to create the proper subclass, Beta, and it inherits all of the methods of superclass, Alpha. If you want to return an object of type Beta you should be declaring it in the Beta class.
If the issue is that you have circular dependencies, then you can semi-declare a needed but not yet declared class inside of another class by using this little trick:
#Class Beta;
It would help if you tried to describe exactly what you're trying to do with your code.
The easiest way would be to write your alpha function:
- (Alpha *)makeAnother {
return [[[Alpha alloc] init] autorelease];
}
and then overwrite that function in your beta class:
- (Alpha *)makeAnother {
return (Alpha*)[[[Beta alloc] init] autorelease];
}
- (Alpha *)makeAnother {
return [[[self class] alloc] init] autorelease];
}
That code works fine. Without you defining what "doesn't work" means to you, hard to say more.
However, you should define the method as returning (id) if you are ever going to expect a pattern like this to work without casting:
Beta* b = [someObjectIKnowIsBeta makeAnother];
Objective-C doesn't support co/contra variance and, thus, any method that is expected to return instances of different classes is declared as returning (id) to avoid a cast. This is common across the frameworks. See -dataCell, +array, +alloc, etc...
Related
Wow, great issue I have found for myself.
What is it? The candy or the garlic?
something about Objective-C:
Are there any issues not to use 'self' in (+) - class methods as class?
in the deep of a class...
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
Ruby here:
For example, in Ruby everything is object and class is object of class Class and there is a good practice to rely on self:
class DateClass
# self is DateClass here, inside of class definition, uh
self.dateWithTimeInterval(interval)
self.dateWithTimeIntervalSince1970(interval)
end
end
Perl here:
Another example was found in perl oop deep: (thanks for this thread)
sub new {
my $proto = shift || die "Must pass a class or object into new()";
my $class = ref($proto) || $proto;
bless {}, $class;
}
So, in Perl and in Ruby guys always rely on $class refs
Maybe example with Perl code not obvious, but it happens all time. Programmers rely on $class reference and take class name with it. also, they can invoke some methods with it:
my $class = 'Class';
$class->new();
or
Class::->new()
After all...
Which pitfalls or caveats could you provide against usage self as class in objective-c?
Usually you use self whenever you can but of course, there are situations when referencing the class by [MyClass class] is desired. Almost all of the scenarios are related to inheritance.
For example, a creator method for a class A.
#implementation A
+ (id)createInstanceWithParam:(NSInteger)param {
return [[self alloc] initWithParam:param];
}
#end
Will work correctly even if we create a subclass B. However, if we decide to implement a class cluster, then we have to reference classes by names:
#implementation SomeDataStructure
+ (id)createInstanceWithType:(NSInteger)type {
if (type == 0) {
return [[DataStructureImpl1 alloc] init];
}
else if (type == 1) {
return [[DataStructureImpl2 alloc] init];
}
}
#end
Another example is the common example of +initialize
+ (void)initialize {
if (self == [MyClass class]) {
...perform initialization...
}
}
And of course, if you are overriding a method, then using self or using [MySelf class] can be a distinction between your overriden implementation and the original implementation. Although super could be used there, too.
TLDR:
self is preferred but be careful with subclasses/superclasses.
For understanding pros and cons of using self vs. class name let's consider one situation:
Class A is subclass of NSDate and implements method +(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval.
Class B is subclass of A and overrides implementation of +dateWithTimeIntervalSince1970:(NSTimeInterval)interval method that declared in NSDate.
Now let's consider two possible implementations of +(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval method in A:
1. Using self
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
if run [B dateWithTimeInterval:interval]; then self in above code is kind of B class and as expected custom implementation (in class B) for +(NSDate*)dateWithTimeIntervalSince1970:(NSTimeInterval)interval method would be called.
2. Using directly NSDate
+(NSDate*)dateWithTimeInterval:(NSTimeInterval)interval {
return [NSDate dateWithTimeIntervalSince1970:interval];
}
if run [B dateWithTimeInterval:interval]; then overridden implementation (in class B) would be ignored and instead of it: original implementation (in class NSDate) for +(NSDate*)dateWithTimeIntervalSince1970:(NSTimeInterval)interval method would be called. It's so because we directly send message to NSDate: [NSDate dateWithTimeIntervalSince1970:interval];.
This behavior is unexpected for developer.
For the same reason declare methods in such way:
+(instancetype)dateWithTimeInterval:(NSTimeInterval)interval {
return [self dateWithTimeIntervalSince1970:interval];
}
By using instancetype compiler will know what kind of object is returned by method-initializer. When you call [B dateWithTimeInterval:interval] it returns object of kind B but not NSDate.
Suppose I have a class BasicDate, and a subclass of BasicDate called EuroDate. The difference between the classes is month-day-year versus day-month-year. I know it'd probably be better to just have methods on the same class to output them differently... but that's not the point of this question.
BasicDate has the following init method:
-(id)initWithMonth:(int)m andDay:(int)d andYear:(int)y {
if(self = [super init]) { /*initialize*/ } return self;
}
And the matching factory method then looks like this:
+(BasicDate)dateWithMonth:(int)m andDay:(int)d andYear:(int)y {
return [[BasicDate alloc] initWithMonth: m andDay: d andYear: y];
}
But if my subclass, EuroDate which would use a factory method more like this:
+(EuroDate)dateWithDay:(int)d andMonth:(int)m andYear:(int)y {
return [[EuroDate alloc] initWithDay: d andMonth: m andYear: y];
} //we can assume that EuroDate includes this init method...
This is all fine. Now, we assume that both classes have their own description method, which will print MMDDYYYY for BasicDate, but DDMMYYYY with EuroDate. This is still all fine.
But if I do this:
EuroDate today = [EuroDate dateWithMonth:10 andDay:18 andYear:2013];
This will call the BasicDate factory method that EuroDate has inherited. The problem is, remember how BasicDate's factory method looks? return [[BasicDate alloc] ...]
So today polymorphs into a BasicDate despite me wanting to store it as a EuroDate, so if I call the description method, it will print 10182013 rather than 18102013.
There are two solutions to this problem I have found.
Solution 1: Change BasicDate's factory method. Rather than return [[BasicDate alloc] ..., I can instead do return [[[self class] alloc] ...] This works and will allow me to use this method for BasicDate or any of BasicDate's subclasses and it will return the right object type.
Solution 2: Override the factory method. Whether I override it to throw an exception or override it to do return [[EuroDate alloc] ...]. The problem with overriding it is that I have to override every factory method for every subclass.
Which is better? What are some downsides to the two possible solutions that I may be missing? What is considered the standard way of handling this issue in Objective C?
You should generally use [[[self class] alloc] init...] in factory methods to ensure that they create instances of the correct class. Note that class isn't a property (and in fact, there's no such thing as a 'class property') so the use of dot syntax there is inappropriate.
Edit
And as pointed out by #ArkadiuszHolko (and Rob, thanks), you should now use instancetype rather than id for the return value, to get the benefits of strong typing while maintaining type flexibility for subclasses. And by the way, Apple's naming conventions suggest avoiding using the word 'and' in method names. So consider rewriting your convenience method like so:
+ (instancetype)dateWithMonth:(int)month day:(int)day year:(int)year
{
return [[self alloc] initWithMonth:month day:day year:year];
}
I have this class:
#interface G2Matrix : NSObject
...
- (id) initWithArray:(float *)val;
...
#end
This line below give me a warning saying that the first argument to the method initWithArray has an incompatible pointer type:
float m[16];
...
G2Matrix* matrix = [[[G2Matrix alloc] initWithArray:m] autorelease];
If I change the method name to something like initWithArray1 the warning disappears. I know that some objects in foundation classes have a method with the same name, but I am deriving from NSObject, which doesn't have this method. What gives?
Additional info - I call the same initWithArray method from other init methods in the G2Matrix class, but I don't see the warning there.
At a guess, this is a type problem:
Inside the other init methods, you call [self initWithArray:...]. self is typed as a G2Matrix*. In this context the compiler can fully resolve which imp (C function pointer) will eventually handle the method call, and detect its signature (argument and return types) correctly.
Out in regular code, [G2Matrix alloc] returns an id. In this context the compiler can only tell the method selector, which will be bound to an imp at runtime. It has to guess which initWithArray: you mean, and as you can see from the warning it guesses wrong, since a foundation class has an initWithArray: method with a different signature. Your code does still work, the compiler just can't be certain.
Picking a unique name for the initMethod (initWithFloats: maybe?) is the recommended way to shut the warning up. Other ways are: break it into two lines; or cast the alloc return value to the right class:
G2Matrix *matrix = [G2Matrix alloc];
matrix = [[matrix initWithArray:pointerToFloats] autorelease];
// or
G2Matrix* matrix = [[(G2Matrix *)[G2Matrix alloc] initWithArray:m] autorelease];
Looks a little odd, but allows you to turn the treat-warnings-as-errors compiler flag back on.
#tathagata thats because initWithArray is method defined in NSArray class so you cannot use it unless you subclass NSArray class.
see the documentation on NSArray
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/NSArray.html
PS.
by use the method, i meant Override the existing method for your purpose which is not a good idea you can find the Subclassing Notes in the document.
I want to initialize an instance of one of the subclasses of a superclass depending on the arguments to init:
[[Vehicle alloc] initWithItinerary: shortWay]; // returns a bicycle
[[Vehicle alloc] initWithItinerary: longWay]; // returns a car
I can't find examples of code like this. I wonder if this is not idiomatic in Objective C, or I simply am not looking in the right places.
You could do this via a custom init method, but it'd be kind of tedious (you'd have to invoke [super init], but then call [self release], etc...). It'd be much simpler to create a class method on Vehicle and use that as your factory method. For example:
+ (id) vehicleWithItinerary:(id)someItinerary {
if ([someItinerary isAShortWay]) {
return [[[Bicycle alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isAMediumWay]) {
return [[[RocketPack alloc] initWithItinerary:someItinerary] autorelease];
} else if ([someItinerary isALongWay]) {
return [[[Car alloc] initWithItinerary:someItinerary] autorelease];
}
return nil;
}
Look at [UIButton buttonWithType:] for an example of how Apple does this. Instead of init, they use a static method of the base class to allocate an instance of the appropriate derived class.
You can also pass around Class objects. Maybe the itinerary knows the Class or class name to allocate. You can do something like this:
[[[itinerary classToAllocate] alloc] initWithItinerary:itinerary];
or
[[NSClassFromString( [itinerary classNameToAllocate] ) alloc] initWithItinerary:itinerary];
You are allowed to release self and create a new object in init, although this is rarely used. Just watch out for recursion.
-(id) initWithItinerary:(Itinerary *)inItinerary {
[self release]; // super init never called - safe if you wrote super classes
self = [[[inItinerary classToAllocate] alloc] init];
self.itinerary = inItinerary;
return self;
}
This is called a class cluster. Several Cocoa classes work this way, including NSArray and NSString. The object returned from NSArray's init methods is never the same object that received the message. It's not that common outside of Cocoa, though, just because it's usually more complicated than people want to bother with. Basically, you figure out what actual class you want to use in your initializer, create an instance of that class, release yourself and return the other instance.
You might want to add an enum to the header file:
typedef enum {Bike, Car, JetPack
} vehicleType
That way your initWithItinerary: method can simply be:
if(VehicleType == Bike)
{
//do bike stuff
}
else if(VehicleType == Car)
{
//do car stuff
}
Why not have a method as part of the "way" that gives you a vehicle of the appropriate type for the way. e.g.
e.g.
// Somwhere before you use them. Car and Bicycle are subclasses of Vehicle
[shortWay setAppropriateVehicleType: [Bicycle class]];
[longWay setAppropriateVehicleType: [Car class]];
// when you need a vehicle
Vehicle* vehicle = [[[shortWay appropriateVehicleType] alloc] init];
I'm going to create a base class that implements very similar functions for all of the subclasses. This was answered in a different question. But what I need to know now is if/how I can cast various functions (in the base class) to return the subclass object. This is both for a given function but also a function call in it.
(I'm working with CoreData by the way)
As a function within the base class (this is from a class that is going to become my subclass)
+(Structure *)fetchStructureByID:(NSNumber *)structureID inContext:(NSManagedObjectContext *)managedObjectContext {...}
And as a function call within a given function:
Structure *newStructure = [Structure fetchStructureByID:[currentDictionary objectForKey:#"myId"]];
inContext:managedObjectContext];
Structure is one of my subclasses, so I need to rewrite both of these so that they are "generic" and can be applied to other subclasses (whoever is calling the function).
How do I do that?
Update: I just realized that in the second part there are actually two issues. You can't change [Structure fetch...] to [self fetch...] because it is a class method, not an instance method. How do I get around that too?
If I understand your question correctly I believe the key is the [self class] idiom.
As far as your update goes requesting a way to call a class method on the current class you can use [self class]. As in:
Structure *newStructure = [[self class] fetchStructureByID:[currentDictionary
objectForKey:#"myId"]];
inContext:managedObjectContext];
EDIT: I redid this to return id per #rpetrich's comment -- much cleaner and avoids the need for -isKindOfClass: as long as you're sure of the type of the instance you're calling -createConfiguredObject on.
As for the first part, you could just return an id (pointer to any object) and document that it will return an instance of the same class it's called upon. Then in the code you need to use [self class] anywhere you instantiate a new object in a method.
e.g. if you have a -createConfiguredObject method which returns an instance of the same class it's called on, it would be implemented as follows:
// Returns an instance of the same class as the instance it was called on.
// This is true even if the method was declared in a base class.
-(id) createConfiguredObject {
Structure *newObject = [[[self class] alloc] init];
// When this method is called on a subclass newObject is actually
// an instance of that subclass
// Configure newObject
return newObject;
}
You can then use this in code as follows:
StructureSubclass *subclass = [[[StructureSubclass alloc] init] autorelease];
subclass.name = #"subclass";
// No need to cast or use isKindOfClass: here because returned object is of type id
// and documented to return instance of the same type.
StructureSubclass *configuredSubclass = [[subclass createConfiguredObject] autorelease];
configuredSubclass.name = #"configuredSubclass";
For reference, what I was referring to with -isKindOfClass: and casting to the proper subclass is as follows:
Structure *structure;
// Do stuff
// I believe structure is now pointing to an object of type StructureSubclass
// and I want to call a method only present on StructureSubclass.
if ([structure isKindOfClass:[StrucutreSubclass class]]) {
// It is indeed of type StructureSubclass (or a subclass of same)
// so cast the pointer to StructureSubclass *
StructureSubclass *subclass = (StructureSubclass *)structure;
// the name property is only available on StructureSubclass.
subclass.name = #"myname";
} else {
NSLog(#"structure was not an instance of StructureSubclass when it was expected it would be.");
// Handle error
}