I discovered an odd scenario that produces a compiler warning in XCode that I don't believe is a valid warning.
As an example I created two classes, ClassA & ClassB, that both have an init method called -initWithSomething:
One takes an (NSDate *) as the "something" and the other takes (NSString *)
Class A
// ClassA.h
#import <Foundation/Foundation.h>
#interface ClassA : NSObject {
}
-(id)initWithSomething:(NSDate *)something;
#end
// ClassA.m
#import "ClassA.h"
#implementation ClassA
-(id)initWithSomething:(NSDate *)something {
if (self = [super init]) {
}
return self;
}
#end
Class B
// ClassB.h
#import <Foundation/Foundation.h>
#interface ClassB : NSObject {
}
-(id)initWithSomething:(NSString *)something;
#end
// ClassB.m
#import "ClassB.h"
#implementation ClassB
-(id)initWithSomething:(NSString *)something {
if (self = [super init]) {
}
return self;
}
#end
Implementation of another class that uses both ClassA & ClassB
#import "ExampleClass.h"
#import "ClassA.h"
#import "ClassB.h"
#implementation ExampleClass
-(void)doSomething {
NSDate *date = [NSDate date];
NSString *string = [NSString stringWithFormat:#"Test"];
ClassA *classA = [[ClassA alloc] initWithSomething:date];
ClassB *classB = [[ClassB alloc] initWithSomething:string]; // Produces "Incompatible pointer types sending 'NSString *' to parameter of type 'NSDate *'
ClassB *classB2 = [[ClassB alloc] initWithSomething:[NSString stringWithFormat:#"Test"]]; // Does NOT produce a warning
ClassB *classB3 = [[ClassB alloc] initWithSomething:#"Test"]; // Produces the same warning as above.
[classA release];
[classB release];
[classB2 release];
[classB3 release];
}
Is this a compiler bug? Doesn't seem like either of those lines should produce a warning, especially since the line where "classB2" is initted produces no warnings.
This code actually works fine, the correct class' -initWithSomething: is called and passed the appropriate type of argument.
Clearly, more explicit method names will avoid the issue but I'd like to know why the compiler isn't able to handle this.
Note:
I should add that this only seems to occur with -init methods, any other instance or class functions do not seem to produce the warning.
I think that the problem is that +alloc returns a generic id.
This means that any method can be called on it, and the compiler will see that the first method imported that has the signature -initWithSomething is for class A, which expects an object of type NSDate *.
Also, I do believe that the method +stringWithFormat returns an id which can be compatible with NSDate.
EDIT:
A simple solution to this problem:
#interface ClassA
+(ClassA *) typeSafeAlloc;
// ...
#end
#implementation ClassA
+(ClassA *) typeSafeAlloc
{
// self is the class variable, which is the same as:
// return [ClassA alloc];
return [self alloc];
}
#end
And repeat the process with ClassB (with typeSafeAlloc returning a ClassB object)
alloc return an object of type id and therefore the compiler assumes that initWithSomething belongs to Class A (The first class interface it encounters that has the method name).
Somthing like
[(ClassB*)[ClassB alloc] initWithSomething:string]; should solve the problem.
Look at the return type of +stringWithFormat
Returns a string created by using a given format string as a template into which the remaining argument values are substituted.
+ (id)stringWithFormat:(NSString *)format, ...
(from http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html)
The compiler knows string is an NSString*, same with #"literals". An id however, could be anything (even an NSSDate*).
Related
I need to move the same method from 4 different classes to the superclass.
Such methods are exactly the same except for the type of a variable declared in them:
For example, in the method in the first class I have
FirstClass var = [[FirstClass alloc] init]
in the second class
SecondClass var = [[SecondClass alloc] init]
and so on.
What's the best way to implement this variation in the superclass ?
Should I use NSClassFromString in the superclass and get each string from each method in the subclasses?
thanks
I'm not 100% sure I get what you mean. So I could be answering the wrong question
If inside your class you need to use an object (I've called it worker below) to do your work, but the class of this object is not known til later, you can use dependency injection (DI).
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, retain) id<WorkerInterface> worker;
#end
MyClass.m
#implementation MyClass
#synthesize worker = _worker;
- (void)myMethod;
{
[self.worker doSomething];
}
// You could also provide a default class to use if one is not passed in
//
// - (id<WorkerInterface)worker;
// {
// if (!_worker) {
// _worker = [[DefaultWorker alloc] init];
// }
// return _worker;
// }
#end
Now whenever I instantiate this class I can simply pass in the appropriate object to be used e.g:
MyWorkerClass *worker = [[MyWorkerClass alloc] init]; // <- Conforms to #protocol(WorkerInterface)
MyClass *instance = [[MyClass alloc] init];
instance.worker = worker;
[instance doSomething];
If all the different types of iVar's you intend on initializing in the subclasses are descended from a common class, then I'd store that class in the super, or else just store it as an id. Then, setup a property accessor in each of your subclasses the casts the iVar as you need it.
#interface superClass : NSObject{
id _superIvar;
}
#end
#implementation superClass : NSObject
....super's code....
#end
Now in the implementation of the subclass declare a property in a category, shown below (or in the interface, if you want it public)
#interface subClass (private)
#property (strong) ClassType *superIvar;
#end;
#implementation
- (void) setSuperIvar:(ClassType *)superIvar{
_superIvar = superIvar;
}
- (ClassType *) superIvar{
return (ClassType *) _superIvar;
}
- (void) someMethodThatUsesSuperIvar{
[self.superIvar doSomething];
}
#end
Alternatively, if you don't want to open your _superIvar to direct access, you can set a property on the superclass and access through the property on the subclass. But in this way you can easily access super's ivars cast to the appropriate type.
I creating my own class with a custom initWithCoord that takes one (struct CLLocationCoordinate2D*) as parameter.
Even though I think I have the correct parameter type, I still get the "incompatible type for argument 1 of 'initWithCoord:" What am I doing wrong:
PeopleStub.m:
#import "PeopleStub.h"
#implementation PeopleStub
-(id)initWithCoord:(struct CLLocationCoordinate2D*)coord{
if(self = [super init]){
people = [[NSMutableDictionary alloc] init];
}
return self;
}
#end
PeopleStub.h
#import <Foundation/Foundation.h>
#interface PeopleStub : NSObject {
NSMutableDictionary *people;
}
-(id)initWithCoord:(struct CLLocationCoordinate2D*)coord;
#end
My way of creating an instance of PeopleStub:
PeopleStub *peopleStub = [[PeopleStub alloc] initWithCoord:locationManager.location.coordinate;
Thanks in advance!
You probably want to be passing just the CLLocationCooridnate2D, not a pointer to same as the parameter. So:
-(id)initWithCoord:(CLLocationCoordinate2D)coord;
I've got an odd situation where I've got two protocols and a couple classes that are initialized with delegates which implement the protocols. Standard stuff, really. However, XCode 4 is tossing up erroneous warnings that I can't seem to wrap my head around. Here's the collapsed source listing showing the definitions and implementations.
// CLASS A
#protocol ClassADelegate <NSObject>
-(void) DoSomething;
#end
#interface ClassA : NSObject { }
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d;
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
#end
#implementation ClassA
+(ClassA *) classWithDelegate:(id<ClassADelegate>)d {
return [[[ClassA alloc]initWithDelegate:d]autorelease];
}
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d{
self = [super init];
return self;
}
#end
// CLASS B
#protocol ClassBDelegate <NSObject>
-(void) DoSomethingElse;
#end
#interface ClassB : NSObject<ClassADelegate> { }
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
-(void) DoSomething;
#end
#implementation ClassB
+(ClassB *) classWithDelegate:(id<ClassBDelegate>)d {
return [[[ClassB alloc]initWithDelegate:d]autorelease];
}
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d{
self = [super init];
return self;
}
-(void) DoSomething {
}
#end
Now here's the crazy part -- in the static constructor of ClassB on this line:
return [[[ClassB alloc]initWithDelegate:d]autorelease];
...the compiler is throwing a warning that doesn't make sense:
"Type 'id <ClassBDelegate>' does not conform to the 'ClassADelegate' protocol"
Am I missing something? The selector clearly sets the type to id<ClassBDelegate>, so d should be correct. Is this just the compiler getting confused?
The compiler is getting confused because you have methods that have the same name but different return/parameter types, and Objective-C is not exactly friends with method overloading.
When the compiler analyses:
[[[ClassB alloc]initWithDelegate:d]autorelease];
it starts with:
[ClassB alloc]
and the return type of +[ClassB alloc] is id, so the expression above is of type id. The next step is to analyse:
[##expression of type id## initWithDelegate:d]
and at this point there are two possible methods:
-(ClassA *) initWithDelegate:(id<ClassADelegate>)d;
-(ClassB *) initWithDelegate:(id<ClassBDelegate>)d;
Both are possible because the receiver is of type id, so it could be either a ClassA instance or a ClassB instance. But, aha!, the protocol of the first parameter differs, so there shouldn’t be any confusion. However, method lookup is based on two variables: whether it’s a class or instance method, and the corresponding selector. In the methods above, both are instance methods and both have the same selector. The compiler has decided to pick the first one.
One fix is to tell the compiler that the object returned by [ClassB alloc] is of type ClassB * instead of the generic type id:
return [[(ClassB *)[ClassB alloc]initWithDelegate:d]autorelease];
and maybe use this pattern in all similar classes.
Another fix is to give those methods different names, e.g. -initWithClassADelegate: and -initWithClassBDelegate:.
I was trying this sample program below.
I'm not calling the +(void)initialise and -(id)init method in the class B.But its getting called automatically.
Is the -(void)initialise is equal to the default constructor in objective C.
Does the [super init] points to the NSObject.
If i'm not using the -(id)init method i'm getting a warning that the class is with incomplete implementation.
ClassA.h
#import <Foundation/Foundation.h>
static int ab;
#interface ClassA : NSObject {
int a;
}
+ (void) initialize;
- (id) init;
- (void) displayNumOfInstance;
- (void) disp;
#end
ClassA.m
#import "ClassA.h"
#implementation ClassA
+ (void) initialize
{
ab=0;
}
- (id) init
{
self = [super init];
if (self!=nil) {
ab++;
}
return self;
}
- (void) displayNumOfInstance
{
NSLog(#"Number of instances of this class:%d",ab);
}
- (void) disp
{
NSLog(#"The value is %d",ab);
}
#end
ClassB.h
#import <Foundation/Foundation.h>
#import "ClassA.h"
#interface ClassB : ClassA {
}
- (void) display;
#end
ClassB.m
#import "ClassB.h"
#implementation ClassB
- (void) display
{
ab=20;
NSLog(#"The value ab is %d",ab);
}
#end
class2.m
#import <Foundation/Foundation.h>
#import "ClassA.h"
int main (int argc, const char * argv[]) {
ClassA *a = [[ClassA alloc]init];
[a disp];
[a release];
ClassB *b = [[ClassB alloc]init];
[b display];
[b release];
ClassA *a1 = [[ClassA alloc]init];
[a1 disp];
[a1 release];
ClassB *b1 = [[ClassB alloc]init];
[b1 display];
[b1 release];
return 0;
}
Output:
2011-04-30 15:31:42.490 class2[1674:a0f] 1
2011-04-30 15:31:42.493 class2[1674:a0f] The value ab is 20
2011-04-30 15:31:42.494 class2[1674:a0f] 2
2011-04-30 15:31:42.495 class2[1674:a0f] The value ab is 20
The default construction usually start with has the following format -init or any variant upon this, e.g. -initWithFrame:.
The method +initialize is a class method (static method) that's called at least once when your application starts. You can use this method to initialize static variables that are useful across all instances of the class. This method might be useful to e.g. initialize a shared cache or a shared lookup map for a class.
For NSObject the -init method is the designated initializer, but for other classes this might differ. Apple documents the designated initializer in it's class headers using the NS_DESIGNATED_INITIALIZER macro. For example UIView subclasses should override -initWithFrame: and -initWithCoder: instead, as these methods are marked as designated initializer.
When subclassing and implementing a custom designated initializer, don't forget to initialize the super class as well. Let's for example have a UIView subclass that has a custom designated initializer -initWithFrame:title:. We would implement it as follows:
// A custom designated initializer for an UIView subclass.
- (id)initWithFrame:(CGRect)frame title:(NSString *)title
{
// Initialize the superclass first.
//
// Make sure initialization was successful by making sure
// an instance was returned. If initialization fails, e.g.
// because we run out of memory, the returned value would
// be nil.
self = [super initWithFrame:frame];
if (self)
{
// Superclass successfully initialized.
self.titleLabel.text = title
}
return self;
}
// Override the designated initializer from the superclass to
// make sure the new designated initializer from this class is
// used instead.
- (id)initWithFrame:(CGRect)frame
{
return [[self alloc] initWithFrame:frame title:#"Untitled"];
}
More details on initialising can be found on the Apple Developer website:
Object Initialization
Multiple Initializers
I have a Class that runs the following method (a getter):
// the interface
#interface MyClass : NSObject{
NSNumber *myFloatValue;
}
- (double)myFloatValue;
- (void)setMyFloatValue:(float)floatInput;
#end
// the implementation
#implementation
- (MyClass *)init{
if (self = [super init]){
myFloatValue = [[NSNumber alloc] initWithFloat:3.14];
}
return self;
}
// I understand that NSNumbers are non-mutable objects and can't be
// used like variables.
// Hence I decided to make make the getter's implementation like this
- (double)myFloatValue{
return [myFloatValue floatValue];
}
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput;
}
#end
When I mouse over the myFloatValue object during debugging, it does not contain a value. Instead it says: "out of scope".
I would like to be able to make this work without using #property, using something other than NSNumbers, or other major changes since I just want to understand the concepts first. Most importantly, I would like to know what mistake I've apparently made.
I can see a couple of mistakes:
The line #implementation should read #implementation MyClass
The function setMyFloatValue is missing a closing ] and } —it should read:
- (void)setMyFloatValue:(float)floatInput{
if ([self myFloatValue] != floatInput){
[myFloatValue release];
myFloatValue = [[NSNumber alloc] initWithFloat:floatInput];
}
}
I've just tested it in Xcode and it works for me with these changes.
Why not just set property in interface and synthesize accessors in implementation?
#interface MyClass : NSObject {
float *myFloat
}
#property (assign) float myFloat;
#end
#implementation MyClass
#synthesize myFloat;
#end