Are the names of some initializer methods like 'initWithRequest' reserved in the API?
I have the following classes
#interface MTURLRequest : NSObject {
...
}
...
#end
and
#class MTURLRequest;
#protocol MTURLRequestHandlerDelegate;
#interface MTURLRequestHandler : NSObject {
...
}
-(id)initWithRequest:(MTURLRequest*)request_ delegate:(id<MTURLRequestHandlerDelegate>)delegate_;
#end
#protocol MTURLRequestHandlerDelegate<NSObject>
...
#end
I get a compile warning at the line where I invoke the initializer
-(void)enqueueRequest:(MTURLRequest*)request_ {
...
MTURLRequestHandler *handler = [[MTURLRequestHandler alloc] initWithRequest:request_ delegate:self];
...
}
warning: incompatible Objective-C types 'struct MTURLRequest *', expected 'struct NSURLRequest *' when passing argument 1 of 'initWithRequest:delegate:' from distinct Objective-C type
There is no NSURLRequest in the code. If I rename the initializer to initWithMTURLRequest everything compiles without warnings. Replacing the forward declaration of the class MTURLRequest with an import statement did not change anything. Clean all targets and rebuild as well. And there is surrely no NSURLRequest *request_ in scope.
There is no 'initWithRequest' initializer in NSObject. At least I can not find any.
Any clue why this happens?
Exact duplicate. See: Can't figure out 'warning: incompatible Objective-C types'
This comment indicates that you haven't grokked a fundamental design points of Objective-C:
OK, thanks. I just renamed the
initializer. Hell, who reviewed the
compiler and did not reject Apples
submission for distributing it? :-) I
understand it that way. There may not
exist two identical selectors within
an application, when the types of
their parameters are different.
The compiler is behaving exactly as designed. Objective-C is designed to be both very simple and very precise in the use of types. Type based dispatch -- as offered by C++ and Java -- is not at all simple, though type precise.
As a part of the simplicity, the (id) type is offered as a generic reference to an object of any class (including metaclasses, technically). Combining that with type safety and multiple declarations of the same method name -- the same selector -- with different arguments leads to ambiguity that the compiler is correctly identifying.
Separating the alloc and init
invocations could be a solution too.
MTURLRequestHandler *requestHandler =
[MTURLRequestHandler alloc];
[requestHandler
initWithRequest:request_
delegate:self]
That isn't quite correct. The -initWithRequest:delegate: method is free to return a different object. Thus, you would need to do:
MTURLRequestHandler *requestHandler = [MTURLRequestHandler alloc];
requestHandler = [requestHandler initWithRequest:....];
This is highly atypical. Casting the return value of +alloc is more typical, though avoiding the issue entirely is the recommended pattern.
Note that the above is also the reason why you should always assign the result of calling super's designated initializer to self.
Related
In the following code
id<SwiftProtocol> anotherInstanceAsProtocol = [[SomeObjectiveCClassImplementingOBJCSwiftProtocol alloc] init];
[anotherInstanceAsProtocol isKindOfClass:[SomeObjectiveCClassImplementingOBJCSwiftProtocol class]];
I get the warning "No known instance method for selector 'isKindOfClass:'"
If I modify the last line to
[(id)anotherInstanceAsProtocol isKindOfClass:[SomeObjectiveCClassImplementingOBJCSwiftProtocol class]]
It runs perfectly.
It also works if I assign to NSObject<SwiftProtocol> instead of id<SwiftProtocol>, but I think neither should be necessary.
Why is this cast necessary?
The problem is that your SwiftProtocol does not inherit from NSObject(Protocol) therefore the Obj-C compiler does not know that there is a method called isKindOfClass:.
Using id basically means you don't want any type checking at compilation time. The real fix should be to make the protocol extend NSObjectProtocol, making sure that all instances conforming to it are normal Obj-C objects.
Note that the history of Objective-C is complicated and not all Objective-C objects have to inherit from NSObject and have isKindOfClass: available.
This is a question on how to gracefully circumvent the nullability of init in NSObject class.
So here is a classic objective-c implementation:
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
static id sharedInstance;
dispatch_once(&onceToken, ^
{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
But now I want to declare it as nonnull, if possible:
+ (nonnull instancetype)sharedInstance;
Unfortunately, init returns a nullable instancetype value. Should I add an NSAssert or something after calling init?
I noticed that some people even document nonnull values as being in reality nullable. Does that make sense?
Should I go bold and simply add NS_ASSUME_NONNULL_BEGIN everywhere, without truly ensuring values are nonnull?
There seem to be two questions here:
How do I ensure that the value produced within my singleton sharedInstance method and returned by that method is actually not nil at run time?
How do I satisfy the system of nullabilty annotations, compiler warnings, and Swift bridging that I'm returning a nonnull pointer?
Ensuring / enforcing nonnull
At some point, every API contract breaks down to a human contract. The compiler can help ensure that, for example, you can't take the result of a nullable call and return it from a method whose return type is nonnull... but somewhere there's usually an original call whose return type is nonnull simply because the programmer who wrote it said, "I promise to never return null, cross my heart, etc."
If you're familiar with Swift, this is similar to the situation with Implicitly Unwrapped Optionals — you use these when you "know" that a value cannot be nil, but can't prove that knowledge to the compiler because that knowledge is external to the source code (something from a storyboard or bundle resource, for example).
That's the situation here — you "know" that init will never return nil, either because you wrote / have source for the initializer in question or because it's just NSObject's init which is documented to return self without doing anything. The only situation where a call to that initializer will fail is because the preceding alloc call failed (and hence you're calling a method on nil, which always returns nil). If alloc is returning nil, you're already in dire straits and your process is not long for this world — this isn't a failure case to be designing API around.
(Nullability annotations are in general for describing intended use of an API, not the more extreme edge and corner cases. If an API call fails only because of a universal error it's not meaningful to annotate it as nullable; likewise, if an API fails only on input that can be ruled out via nonnull parameter annotations, the return value can be assumed nonnull.)
So, long story short: yes, just put NS_ASSUME_NONNULL around your header, and ship your sharedInstance implementation as is.
Returning a nonnull nullable
It's not the case here, but suppose you have a value that's annotated as nullable, but you know (or "know") that it can never be nil and want to return it from your nonnull-annotated method. And you're in a situation where you get a compiler warning for trying.
There's a syntax for that — just cast the value to the expected return type, annotations and all:
return (NSWhatever *_Nonnull)whatever;
In your case this shouldn't be needed — because you're dealing with the id and instancetype special types the compiler is more forgiving about nullability conversion and probably won't warn to begin with.
I'm trying to cast a Class object to a certain protocol, which defines class methods (+) that that class implements.
I know how to do this with (id< protocol>), as outlined in this question, but I can't seem to figure out the right way for Class objects.
The basic scenario is as follows.
I have a protocol:
#protocol Protocol <NSObject>
+ (id)classMethod:(id)arg;
#end
I then have a function which accepts a Class object, which it knows sometimes conforms to the protocol based on another argument (this is obviously very simplified):
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if (arg != nil) {
instance = [(Class<Protocol>)cls classMethod:arg];
}
}
Now I don't get any warnings on this, and it looks right to me. (I'm not going to see any errors in any case, because I can guarantee that if arg != nil then the class conforms.)
However, I'm not getting autocompletion in Xcode, which makes me wonder if this is the right way to do it. Any thoughts? (Note that I am not interested in instance being id< Protocol>.)
If you want to determine whether cls conforms to a particular protocol (and assuming that classMethod: is a required class method of that protocol), you can simply:
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if ([cls conformsToProtocol:#protocol(Protocol)]) {
instance = [cls classMethod:arg];
}
return instance;
}
Alternatively, just see if it responds to a particular class method selector:
- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
id instance;
if ([cls respondsToSelector:#selector(classMethod:)]) {
instance = [cls classMethod:arg];
}
return instance;
}
The question is 11 years old and there is nothing wrong with the Rob's answer, but I find it unfortunate that the centrepiece part of it (whether type-casting a Class object with a protocol is a correct syntax) never got proper attention.
First of all static typing in Objective-C is very artificial thing, and it exists solely for the compiler to emit a warning (not even an error). Let's start with what Class objects really is - if you take a look at the documentation, you will find that the Class type is actually an alias for objc_class * type:
typedef struct objc_class *Class;
You can find definition of objc_class type in the source codes of Apple's objc runtime library:
// inherits objc_object with some adjustments
struct objc_class : objc_object { ... }
As you can see, objc_class is just an extension to a objc_object. Any Objective-C class is in fact instance of this objc_object. E.g. here is how NSObject or id aliases look like:
// "translation" of an Objective-C class declaration
typedef struct objc_object NSObject;
// the same for `id` type but with the pointer type included
typedef struct objc_object *id;
It means that "static typing" doesn't exist in Objective-C, the "typing" of an instance happens via introspection of a given instance (different kind of meta-information objc_object stores). It makes all Objective-C classes compatible with each other (first - because it's a pointer, second - because it's a pointer to the same structure). E.g. you can write code like this:
Class obj = [NSObject new];
..and it will happily compile.
However this purely dynamic nature of the language makes it very error-prone, exposing all kinds of mistakes a programmer can make. In order to avoid that clang in fact does compile time checking of the specified types, but it purely relies on the programmer to provide correct data for a type of an instance, and if the types are incompatible from Objective-C perspective, the compiler can emit a warning for you. This works for instance objects, but unfortunately there is no syntax in Objective-C to type a class object other than with the Class alias. It means that for the compiler all such objects are indistinguishable during compile time.
And all of this is true for protocols typing. Here I mean that when you add a protocol conformance token to a variable type (id<TDWLoadable> var) you merely ask the compiler to check whether the assigned to the variable object conforms to the given protocol:
#protocol TDWLoadable
+ (void)classMethod;
- (void)instanceMethod;
#end
#interface TDWObject : NSObject
#end
// Initializing '__strong id<TDWLoadable>' with an expression of incompatible type 'TDWObject *'
id<TDWLoadable> loadable = [TDWObject new];
For a class object, however, the same check is just ignored, because Class objects cannot be typed:
Class<TDWLoadable> loadable = [[TDWObject new] class];
This behavior is described in the Type Checking section of Protocols part in The Objective-C Programming Language (emphasis mine):
...the declaration
id <Formatting> anObject;
groups all objects that conform to the Formatting protocol into a type, regardless of their positions in the class hierarchy. The compiler can make sure only objects that conform to the protocol are assigned to the type.
In each case, the type groups similar objects—either because they share a common inheritance, or because they converge on a common set of methods.
The two types can be combined in a single declaration:
Formatter <Formatting> *anObject;
Protocols can’t be used to type class objects. Only instances can be statically typed to a protocol, just as only instances can be statically typed to a class. (However, at runtime, both classes and instances respond to a conformsToProtocol: message.)
Also, if we take into account that objc_class is in fact just an extension to objc_object then two expressions of kind:
Class<TDWLoadable> classObj;
id<TDWLoadable> obj;
Should follow the same contract (i.e. + (void)classMethod has to refer to metaclass of classObj and - (void)instanceMethod to the class object itself).
Having that said, since the syntax essentially has no effect and just ignored by the compiler, you are free to come up with your own convention to the Class<Protocol> typing.
Clang adds a keyword instancetype that, as far as I can see, replaces id as a return type in -alloc and init.
Is there a benefit to using instancetype instead of id?
Yes, there are benefits to using instancetype in all cases where it applies. I'll explain in more detail, but let me start with this bold statement: Use instancetype whenever it's appropriate, which is whenever a class returns an instance of that same class.
In fact, here's what Apple now says on the subject:
In your code, replace occurrences of id as a return value with instancetype where appropriate. This is typically the case for init methods and class factory methods. Even though the compiler automatically converts methods that begin with “alloc,” “init,” or “new” and have a return type of id to return instancetype, it doesn’t convert other methods. Objective-C convention is to write instancetype explicitly for all methods.
Emphasis mine. Source: Adopting Modern Objective-C
With that out of the way, let's move on and explain why it's a good idea.
First, some definitions:
#interface Foo:NSObject
- (id)initWithBar:(NSInteger)bar; // initializer
+ (id)fooWithBar:(NSInteger)bar; // class factory
#end
For a class factory, you should always use instancetype. The compiler does not automatically convert id to instancetype. That id is a generic object. But if you make it an instancetype the compiler knows what type of object the method returns.
This is not an academic problem. For instance, [[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData] will generate an error on Mac OS X (only) Multiple methods named 'writeData:' found with mismatched result, parameter type or attributes. The reason is that both NSFileHandle and NSURLHandle provide a writeData:. Since [NSFileHandle fileHandleWithStandardOutput] returns an id, the compiler is not certain what class writeData: is being called on.
You need to work around this, using either:
[(NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput] writeData:formattedData];
or:
NSFileHandle *fileHandle = [NSFileHandle fileHandleWithStandardOutput];
[fileHandle writeData:formattedData];
Of course, the better solution is to declare fileHandleWithStandardOutput as returning an instancetype. Then the cast or assignment isn't necessary.
(Note that on iOS, this example won't produce an error as only NSFileHandle provides a writeData: there. Other examples exist, such as length, which returns a CGFloat from UILayoutSupport but a NSUInteger from NSString.)
Note: Since I wrote this, the macOS headers have been modified to return a NSFileHandle instead of an id.
For initializers, it's more complicated. When you type this:
- (id)initWithBar:(NSInteger)bar
…the compiler will pretend you typed this instead:
- (instancetype)initWithBar:(NSInteger)bar
This was necessary for ARC. This is described in Clang Language Extensions Related result types. This is why people will tell you it isn't necessary to use instancetype, though I contend you should. The rest of this answer deals with this.
There's three advantages:
Explicit. Your code is doing what it says, rather than something else.
Pattern. You're building good habits for times it does matter, which do exist.
Consistency. You've established some consistency to your code, which makes it more readable.
Explicit
It's true that there's no technical benefit to returning instancetype from an init. But this is because the compiler automatically converts the id to instancetype. You are relying on this quirk; while you're writing that the init returns an id, the compiler is interpreting it as if it returns an instancetype.
These are equivalent to the compiler:
- (id)initWithBar:(NSInteger)bar;
- (instancetype)initWithBar:(NSInteger)bar;
These are not equivalent to your eyes. At best, you will learn to ignore the difference and skim over it. This is not something you should learn to ignore.
Pattern
While there's no difference with init and other methods, there is a difference as soon as you define a class factory.
These two are not equivalent:
+ (id)fooWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
You want the second form. If you are used to typing instancetype as the return type of a constructor, you'll get it right every time.
Consistency
Finally, imagine if you put it all together: you want an init function and also a class factory.
If you use id for init, you end up with code like this:
- (id)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
But if you use instancetype, you get this:
- (instancetype)initWithBar:(NSInteger)bar;
+ (instancetype)fooWithBar:(NSInteger)bar;
It's more consistent and more readable. They return the same thing, and now that's obvious.
Conclusion
Unless you're intentionally writing code for old compilers, you should use instancetype when appropriate.
You should hesitate before writing a message that returns id. Ask yourself: Is this returning an instance of this class? If so, it's an instancetype.
There are certainly cases where you need to return id, but you'll probably use instancetype much more frequently.
There definitely is a benefit. When you use 'id', you get essentially no type checking at all. With instancetype, the compiler and IDE know what type of thing is being returned, and can check your code better and autocomplete better.
Only use it where it makes sense of course (i.e. a method that is returning an instance of that class); id is still useful.
Above answers are more than enough to explain this question. I would just like to add an example for the readers to understand it in terms of coding.
ClassA
#interface ClassA : NSObject
- (id)methodA;
- (instancetype)methodB;
#end
Class B
#interface ClassB : NSObject
- (id)methodX;
#end
TestViewController.m
#import "ClassA.h"
#import "ClassB.h"
- (void)viewDidLoad {
[[[[ClassA alloc] init] methodA] methodX]; //This will NOT generate a compiler warning or error because the return type for methodA is id. Eventually this will generate exception at runtime
[[[[ClassA alloc] init] methodB] methodX]; //This will generate a compiler error saying "No visible #interface ClassA declares selector methodX" because the methodB returns instanceType i.e. the type of the receiver
}
You also can get detail at The Designated Initializer
**
INSTANCETYPE
**
This keyword can only be used for return type, that it matches with return type of receiver. init method always declared to return instancetype.
Why not make the return type Party for party instance, for example?
That would cause a problem if the Party class was ever subclassed. The subclass would inherit all of the methods from Party, including initializer and its return type. If an instance of the subclass was sent this initializer message, that would be return? Not a pointer to a Party instance, but a pointer to an instance of subclass. You might think that is No problem, I will override the initializer in the subclass to change the return type. But in Objective-C, you cannot have two methods with the same selector and different return types (or arguments). By specifying that an initialization method return "an instance of the receiving object," you would never have to worry what happens in this situation.
**
ID
**
Before the instancetype has been introduced in Objective-C, initializers return id (eye-dee). This type is defined as "a pointer to any object". (id is a lot like void * in C.) As of this writing, XCode class templates still use id as the return type of initializers added in boilerplate code.
Unlike instancetype, id can be used as more than just a return type. You can declare variables or method parameters of type id when you are unsure what type of object the variable will end up pointing to.
You can use id when using fast enumeration to iterate over an array of multiple or unknow types of objects. Note that because id is undefined as "a pointer to any object," you do not include an * when declaring a variable or object parameter of this type.
The special type instancetype indicates that the return type from the init method will be the same class as the type of object it is initializing (that is, the receiver of the init message). This is an aid for the compiler so that it can check your program and flag potential
type mismatches—it determines the class of the returned object based on context; that is, if you’re sending the init message to a newly alloc’ed Fraction object, the compiler will infer that the value returned from that init method (whose return type has been declared as type instancetype) will be a Fraction object. In the past the return type from an initialization method was declared as type id. This new type makes more sense when you consider subclassing, as the inherited initialization methods cannot explicitly define the type of object they will return.
Initializing Objects, Stephen G. Kochan, Programming in Objective-C, 6th Edition
I have a method like this which performs further initializations on other objects with similar init methods.
- (Foo*)initWithInput:(NSMutableString*)i {
bar = [[Bar alloc] initWithInput:input];
return self;
}
This produces a warning "Foo.m:19: warning: incompatible Objective-C types assigning 'struct Foo *', expected 'struct Bar *'"
I am assuming the alloc method is returning an (id) type and then the compiler can't decide which initWithInput: method I want to use on the allocated object. I can of course remove the warning like this but it's not terribly pretty
- (Foo*)initWithInput:(NSMutableString*)i {
bar = [((Bar*)[Bar alloc]) initWithInput:input];
return self;
}
Is there an idiomatic way of handling this?
Is there an idiomatic way of handling
this?
Objective-C has a flat namespace for selectors and does not support covariant dispatch (a la Java and C++). Thus, you need to name your methods uniquely per argumentation.
This is also the standard pattern of the frameworks. You [generally -- there are 1 or 2 exceptions] don't see two methods with the same name -- the same selector -- with different argumentation. When it does happen -- like the return value of alloc -- you'll see id used.
All init-methods should return id and not the type of the class they belong to for exactly this reason. This is actually documented by Apple somewhere. I can’t find the link at the moment, though.