Objective C: warning on overriding init - objective-c

I have a class 'DOInstance' which I inherit later on. Here's its declaration:
#interface DOInstance : NSObject {
}
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
#end
Then I have a subclass of DOInstance:
#interface Workflow_Workitem_Header_1px: DOInstance {
}
//- (Workflow_Workitem_Header_1px *) initWithSynckey:(NSString *)_synckey;
#end
I go ahead and implement it in the implementation file:
- (Workflow_Workitem_Header_1px *) initWithSynckey:(NSString *)_synckey {
[super initWithSynckey:_synckey];
//..
//..
return self;
}
Now, If I do not declare initWithSynckey: (the commented declaration above) in my subclass declaration, I get a warning at the implementation: "warning: initialization from distinct Objective-C type". If I declare it, this warning goes away. Okay.
Moving on:
I later do an instantiation of my subclass:
Workflow_Workitem_Header_1px *instance;
instance = [[Workflow_Workitem_Header_1px alloc] initWithSynckey:#"xxxx"];
Now, this gives me the same warning (irrespective of whether or not I declare the corresponding initWithSynckey: selector in my subclass. Namely, "warning: initialization from distinct Objective-C type".
What am I doing wrong?

Methods named init... should have return type (id), not the type of the class. Check out NSString.h and NSArray.h (among other classes) for examples. That may be what is causing your problem.

In this case, the overriding method must return the same type as the superclass' declaration.
DOInstance defines this:
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
so Workflow_Workitem_Header_1px must look like this:
#interface Workflow_Workitem_Header_1px: DOInstance {
}
- (DOInstance *) initWithSynckey:(NSString *)_synckey;
#end
Any time you get the warning "warning: initialization from distinct Objective-C type" you're doing something in contravention of your typing: changing a method signature, and the like.

Related

How to initialize a subclass when the init method of the superclass is NS_UNAVAILABLE

I'm attempting to write a test for an objective-c class. The class I'm trying to test is MyClass and it looks like this:
#interface MyClass : NSObject
- (void)dispatchEvent:(IMAAdEvent *)event;
#end
In order to test this dispatchEvent method, I need to pass in an instance of IMAAdEvent. The IMAAdEvent class comes from Google's library GoogleAds-IMA-iOS-SDK.
Unfortunately, I can't call init on this class because the init method is marked as NS_UNAVAILABLE. In XCode I get an error that reflects this:
'init' in unavailable
Ideally, I would like to make my own mock subclass of IMAAdEvent like this. Is there some way I can initialize my subclass without calling the unavailable init method on the superclass?:
#interface MockImaAdEvent : IMAAdEvent
#end
#implementation MockImaAdEvent
- (id)init {
// is there something I can do here so that I return an instances
// of the subclass without calling [super init]?
}
#end
As of Xcode 12.5, Swift is no longer able to use the previous solution. The compiler has started returning errors for init() is unavailable on lines of code where new init functions have been added. The Xcode 12.5 Release Notes indicate the following:
Clang now infers the availability of +new from availability annotations on -init methods. Since +new calls [[Foo alloc] init], +new isn’t available unless +init is available.
Despite this release note, there is still a valid workaround. By writing the mock class in Objective-C and using a bridging-header to bring it into Swift, the mock class can still call super.new to get a new instance of the parent class (and then customize the subclass from there).
Here is an example:
#interface MockInAppMessagingCampaignInfo ()
#property (strong, nonatomic) NSString *campaignNameValue;
#end
#implementation MockInAppMessagingCampaignInfo
+ (id)newWithCampaignName:(NSString *)campaignName {
MockInAppMessagingCampaignInfo *newObject = super.new;
newObject.campaignNameValue = campaignName;
return newObject;
}
- (NSString *)campaignName {
self.campaignNameWasCalled = YES;
return self.campaignNameValue ?: #"";
}
#end
If I use a method that's not called init then this seems to work. It still seems really weird to not call [super init] in this function, but it's working and returning a new instance of the MockImaAdEvent class
#interface MockImaAdEvent : IMAAdEvent {
enum IMAAdEventType type;
}
#property (nonatomic) enum IMAAdEventType type;
#end
#implementation MockImaAdEvent
#synthesize type;
- (id)initWithType:(NSInteger)_type {
type = _type;
return self;
}
#end
// in my test I can initialize like this:
MockImaAdEvent *adEvent = [[MockImaAdEvent alloc] initWithType:kIMAAdEvent_LOADED];
An alternative solution for Xcode 12.5 that doesn't require Objective-C bridging-header is to create a custom static initialiser that uses the objc runtime to invoke new.
I have used that for fakes that subclass objects with unavailable initialisers and works great.
Example:
static func customInit() -> SomeObjectFake {
let instance = SomeObjectFake.perform(NSSelectorFromString("new")).takeRetainedValue() as! SomeObjectFake
...
return instance
}

Pros and cons of using "id" as the return type of a custom "init" method, instead of a pointer to that class?

Assume the following Objective-C class:
#interface Appliance : NSObject
{
NSString *productName;
int voltage;
}
#end
What are the pros and cons of implementing init method A instead of B?
A) -(id)initWithName:(NSString *)name;
B) -(Appliance *)initWithName:(NSString *)name;
I see they both work in XCode, i.e. they both will result in a valid Appliance instance. "A" seems to be the standard among books I've read and codebases I've looked at, and I'm wondering why this is.
Point in fact, for quite some time the best practice return type from a class initializer (in Objective-C) is instancetype instead of id.
Oh, reopen. :-)
Indeed, you did not ask for the difference id vs. instancetype. And for -init… the answer to this non-asked Q would be easy: There is no difference, because the compiler converts id to instancetype silently.
You asked for id vs. CustomClass*. And you get a completely different answer from me: With CustomClass* a subclass had to cast the result of the superclass' designated initializer. Let's have an example:
#interface BaseClass : NSObject
- (BaseClass*)initWithWhatever; // Typed to class, designated initializer
#end
#implementation BaseClass
- (BaseClass*)initWithWhatever // Typed to class
{
self = [super init]; // What's the return type of -init (NSObject)?
…
}
#end
#interface Subclass : BaseClass
// First problem: I like it to announce in the interface, that a class overwrites
// a method of the base class. Doing so I would have to change the return type. Ugly.
// If I do not redeclare -initWithWhatever it is inherited from BaseClass, still
// having BaseClass* as the return type. Is that the truth? Really?
// However, I do not overwrite it here, but have a new initializer.
- (Subclass*)initWithSomethingElse;
#end
#implementation Subclass
- (Subclass*)initWithSomethingElse
{
// Second Problem:
// First, I have to execute the superclass' designated initializer
self = [super initWithWhatever];
// Wait a minute!
// self is a reference to Subclass. The return value of -initWithWhatever has the type
// BaseClass*. So I assign a reference of the base class to a reference of the subclass:
// Compiler error, false positive. The code is correct.
// So I would have to cast. Ugly, ugly, ugly.
#end
…
// Third problem:
Subclass *object = [[Subclass alloc] initWithWhatever];
// Typing -initWithWhatever to BaseClass* would lead to a compiler error here again.
// Compiler error, false positive. The code is correct.
To make the long story short: Without a mass of castings it would be impossible to type initializers to the concrete class.

Method parameters, protocol methods, delegates and respondsToSelector calls

I have this kind of code :
MyClass.h
#import "MyOtherClass.h"
#interface MyClass : NSObject<SomeProtocol, MyOtherClassDelegate> {
...
}
MyClass.m
+ (void) doThis {
[someObject doThisWithDelegate:self]; // someObject is MyOtherClass type
}
MyOtherClass.h :
#protocol MyOtherClassDelegate <NSObject>
#required
// Some methods
#end
- (void) doThisWithDelegate:(id<SomeOtherProtocol>)delegate;
MyOtherClass.m :
- (void) doThisWithDelegate:(id<SomeOtherProtocol>)delegate {
if ([delegate respondsToSelector:#selector(myProtocolMethod:error:)]) do things;
}
Doing like this, I have the following warnings at compile time :
on the line [someObject doThisWithDelegate:self];
"Incompatible pointer types sending Class to parameter of type id<SomeOtherProtocol>"
on the method declaratéin in MyOtherClass.h :
"Passing argument to parameter 'delegate' here"
Before, I hadn't typed the id param (with id<SomeOtherProtocol>), it was just "alone" (id). I noticed that the test :
if ([delegate respondsToSelector:#selector(myProtocolMethod:error:)])
returned FALSE (but of course methods are implemented and declared in the delegate).
So I decided to try to force the id type to conform protocol that causes me that warning at compile time.
What is happening here ?
Why do I have this error, and why do the respondsToSelector do not return TRUE ?
If you want to call respondsToSelector: on a property with a type of id<SomeProtocol> then make sure your protocol conforms to the NSObject protocol.
Example:
#protocol SomeOtherProtocol <NSObject>
// methods
#end
This will then allow you to do:
if ([self.delegate respondsToSelector:#selector(myProtocolMethod:error:)]) {
}
This assumes delegate is defined as:
#property (nonatomic, weak) id<SomeOtherProtocol> delegate;
First make sure you conform to , respondsToSelector is part of NSObject.
Second I would check that i had effectively set the delegate property.
This line :
if ([self.delegate respondsToSelector:#selector(myProtocolMethod:error:)])
should work, since the property delegate is pointing to an object that should have myProtocolMethod:error: ....
I would probably debug with a breakpoint or a test on a setter for the delegate :
-(void)setDelegate:(id)delegate
{
NSLog("We are setting a delegate!: %#", [delegate description]);
_delegate = delegate;
}
If when you run your app, you see the "We setting...." after you do your if line or if you don't see it at all, then you know where your issue is.
Problem was that I've declare the doThis method as a class method and not instance method. So self is not valid when passed as a parameter.

Property/Synthesize errors

Sorry to keep asking basic questions here but I don't know where else to go. Wrote some code with a slider, textfield and buttons for incrementing the slider to demonstrate key value coding. Everything worked find. The next step was to use 'property' and 'synthesize' in place of the accessor and setter methods;
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
#end
~~~~~
#implementation KVCController
#synthesize fido;
- (id)init{
self = [super init];
if (self) {
// Initialization code here.
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
}
return self;
}
~~~~~~~
#end
I get an incomplete implementation error on #implementation KVCController. If I put the get and set methods for 'fido' in it clears up.
The second error occurs with #synthesize fido;. It says property must be declared in the implementation. Everything is copied correctly out of the book and near as I can tell, it looks just like all the other uses of property and synthesize I have looked at. Anyone have any ideas on what I am missing or doing wrong?
Xcode 4.1 automatically creates a delegate class which I usually ignore if I am not working on delegates. I created my own class for the KVC exercise and just added the property/synthesize declarations to it with appropriate modifications and got the errors. I just put the property/synthesize declarations into the delegate class, moved my IBAction code to the appropriate places, redid the bindings, and erased the class I created and everything worked fine. Do property/synthesize declarations need to be treated like delegate material?
incomplete implementation means you have a -(void)something that may be defined in your header that you are not using in your #implementation. Make sure that you do not have any unused methods listed in your header. if you do, either remove them from the header, or create the method in your implementation.
- (void) dosomething
{
/* blank for now */
}
if you have -(void)dosomething in your implementation, define it in your header.
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
- (void) dosomething;
#end

Odd or erroneous warning with multiple #protocols

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:.