Why can dot syntax not be used on a method whose return type is instancetype? - objective-c

Pretend I have a category on NSObject that defines the following method:
+ (instancetype)allocTemplate
{
id instance = [self new];
return instance;
}
and I have the following class:
#interface FDActor : NSObject
#property (nonatomic, copy) NSString *name;
+ (void)sayHi;
#end
#implementation FDActor
+ (void)sayHi
{
[self allocTemplate].name;
}
#end
How come [self allocTemplate].name errors out at compile time if self is FDActor?
I am aware it works if you use normal message sending syntax but I am explicitly interested in the dot syntax error.

It would appear as though instancetype is used only for type checking during assignment, so
FDActor *actor = [FDActor allocTemplate] would not produce a warning.
If we cast the return type of allocTemplate the problem goes away.
- (void)sayHi
{
((__typeof__(self))[[self class] allocTemplate]).name;
}
But note that this only works in an instance method since the typeof an instance is another instance. Note also that since we now explicitly type the return value the allocTemplate method is no longer necessary, if all were looking for is type checking then we can even just cast nil and it will work.
If we try the same thing in a class method it doesn't work
+ (void)sayHi
{
((__typeof__(self) *)[self allocTemplate]).name;
}
This is because (__typeof__(self) *) doers not evaluate to FDActor * but Class * which ARC will complain about. It looks like there is no way to resolve the type information FDActor * in a generic way from within a class method at compile time.
I rather expected the instancetype keyword to be a little more useful.

The error message tells you. [self allocTemplate] is an id. Property syntax never works on id.
Note that this does work:
[FDActor allocTemplate].name;
I think the class to which instancetype is to be related must be specified in the call; otherwise we just fall back on id.

I'll take a stab at this, with the disclaimer that I don't fully understand how instancetype support is actually implemented, let alone all the nitty gritty details of the compiler's type checking system. In other words, this is my best guess, and not an authoritative or complete answer...
After compilation, Objective-C methods are actually functions, where self is the first argument to the function, and is typed id. So you have:
void sayHi(id self, SEL _cmd)
{
[self allocTemplate].name; // Actually calls to objc_msgSend, but that doesn't matter here
}
The compiler's treatment of the type of value returned by +allocTemplate here seems to be related to this. If you change self to an explicit FDActor the error goes away. Of course, for methods -- as opposed to properties -- the compiler types self as you'd expect, and will warn (or error under ARC) for methods that self doesn't appear to respond to. It seems like this is perhaps a difference (bug?) in the compiler's checking for available methods vs. properties in the context of instancetype.

Related

Circular references in Objective-C and clang

Why doesn't the static analyser detect circular references with blocks? I remember it used to do it when I had retains on my delegates instead of assign, pre-blocks introduction. I remember the nice little lines it used to draw on my code (i think...)
If I do this, without using weakSelf, I know I'm going to get a circular reference.
// Note 1: myObject is 'retained' by self.
// Note 2: myObject retains the block for the future
[self.myObject registerBlockOfCodeForFutureExectution:^{
[self doSomething];
}];
Sample Project Exploiting Issue
Now if I know this, and I'm a stupid human, then why doesn't my intelligent computer know this is bad and warn me that I'm being stupid?
There must be a logical reason why it can't detect it, and I want to know what that reason is.
This question is regarding clang and static analysis, please don't suggest how I fix circular references - I know how to do this.
If you use self inside of block it does not automatically mean that you get retain cycle. You get retain cycle only if life-time of block depends on life-time of self object. That may be the case if self has strong reference to myObject or some more complex dependencies are also possible (I assume that it indeed 'saves' block passed to a method, so you already have strong reference there).
So to have retain cycle in your example you need to have two following conditions met (neither of them follows from the code you posted), and compiler needs to be aware of them:
1. Life-time of myObject is tied to self - lets assume that self has strong reference to it
2. saveThisBlockInMyObject: retains block passed to it
I made a small sample that gives compiler warning about capturing self - to address 1st point I declared myObject as a strong property of some class:
#property (strong) MyTestClass* myObj;
...
self.myObj = [MyTestClass new];
For 2nd point I could not find a way to specify that method retains its argument (there're source annotations for returned values, but there's no relevant annotations for method parameters). But you declare block as a strong property of your test class, then compiler is happy to warn you about possible retain cycle:
typedef void (^MyVoidBlock)();
// MyTestClass
#property (nonatomic, copy) MyVoidBlock voidBlock;
self.voidBlock = ^{
[self doSomething]; // Warning!
};
Hope that makes sense :)
The code posted to github does cause a retain cycle.
Current github code:
#interface MyObject ()
#property (nonatomic, copy) dispatch_block_t codeToRunInFuture;
#end
#implementation MyObject
- (void) registerBlockForFuture:(dispatch_block_t)block {
self.codeToRunInFuture = block;
}
// Call in ViewController
self.myObject = [MyObject.alloc init];
[self.myObject registerBlockForFuture:^{
[self runThisInFuture];
}];
I can see where this would be difficult to catch since the Analyzer can not know what block might be and therefore can not tell if there is a self reference either strong or weak. It would have to examine all instances where registerBlockForFuture: is called and the block in each case.
The answer might be to submit a bugreport to Apple.

Protocol or class specific selector in objective-c

Using a selector to call a method that exists in more that one class (but with different signatures on return or argument types) causes an Multiple methods named [method name] found... error.
This has been already settled in other questions:
Defeating the "multiple methods named 'xxx:' found" error
Issue with "Multiple methods named...."
Multiple methods named "count" found with mismatched result, parameter type or attributes
A related problem happens if the repeated method is in a protocol. Having a strongly typed protocol object is still ambiguous for the compiler since the object could also be an instance of other classes implementing the same-ish signature method:
#protocol ExistingMethodProtocol <NSObject>
// This method also exists in UIView, but with a different return type
- (NSString*)contentMode;
#end
#interface ImplementingClass : NSObject <ExistingMethodProtocol>
#end
#implementation ImplementingClass
- (NSString*)contentMode {return nil;}
- (void)problematicCall
{
// Multiple methods named... error
[self performSelector:#selector(contentMode)];
id<ExistingMethodProtocol> idOfProtocol = self;
// Multiple methods named... error too, even casted
[idOfProtocol performSelector:#selector(contentMode)];
}
#end
An alternative is to create the selector separately and then perform, thus bypasing the compiler check but causing a warning:
SEL selector = #selector(contentMode);
// Warning: PerformSelector may cause a leak because its selector is unknown
[object performSelector:selector];
What other alternatives could work here for checking and performing a method when a protocol has a method colliding with a same-ish signature?
my 2 cents. Use Runtime reference. In which case it bypasses the compiler warnings/error. Looks like there are several ways to do this, here is my solution.
#import <objc/message.h>
struct objc_method *method = class_getInstanceMethod([self class], #selector(contentMode));
id answer = method_invoke(self, method);
This should prevent errors from selectors in different protocols named the same. But I am a little unsure how this behaves with methods with different implementations in base and super classes.

Deduction of instancetype by LLVM compiler

I have this piece of code:
#interface Bar : UIView
- (id)initWithInt:(int)i;
#end
#implementation Bar
- (id)initWithInt:(int)i {
self = [super init];
return self;
}
#end
void func() {
[[[Bar alloc] initWithInt:10] doSomething];
}
The compiler gives me an error in the func() function: No visible #Interface for Bar declares the selector doSomething.
I do have another #interface with the method doSomething.
The compiler somehow assumed that initWithInt: returns instancetype rather than id.
What are the rules to this? In what cases does a compiler assume that a method returns instancetype?
In what cases does a compiler assume that a method returns
instancetype?
That is documented in "CLANG LANGUAGE EXTENSIONS":
According to Cocoa conventions, Objective-C methods with certain names
(“init”, “alloc”, etc.) always return objects that are an instance of
the receiving class’s type. Such methods are said to have a “related
result type”, meaning that a message send to one of these methods will
have the same static type as an instance of the receiver class.
...
To determine whether a method has an inferred related result type, the first word in the
camel-case selector (e.g., “init” in “initWithObjects”) is considered,
and the method will have a related result type if its return type is
compatible with the type of its class and if:
the first word is “alloc” or “new”, and the method is a class method,
or the first word is “autorelease”, “init”, “retain”, or “self”, and
the method is an instance method.
So your initWithInt: method has a "inferred related result type", and
therefore the compiler assumes that it returns an instance of Bar.
don't know BUT
inits always should return instance types anyways. it is only logical. I cannot imagine any other situation.
Just import the subclass that declares doSomething and you're good
I do have another #interface with the method doSomething.
Each class must have exactly one #interface (although you can make class extension, categories, etc. to add further methods). If your -doSomething method is in a category (or class extension) in another file, make sure that its declaration is visible in this file via an #import.

Why do I have to cast the delegate?

I have a class and this class has a delegate protocol. I create an object in the main class using this class and assigned the main class as the delegate. The main class has a property I would like to read inside the created class. Then I do this:
BOOL prop = [self.delegate myProperty];
Xcode complains that "delegate may not respond to myProperty"
If I am sending a message to self.delegate to read myProperty, and generally Xcode never complains when you send a message to an not casted object, why do I have to do that
BOOL prop = [(myMainClass *)self.delegate myProperty];
to make it work?
To be more clear, here is an example of a message sent to an object without having to cast:
[self.myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
int Number = [[obj dictionaryOfNames] count];
}];
self.myArray is an array of objects from another class that are dictionaries and have this property dictionaryOfNames. Xcode never asked me to cast this:
int Number = [[(myOtherClass *)obj dictionaryOfNames] count];
as I had to above for myProperty.
Different classes can conform to a protocol. If you declare that you conform to a protocol you just say that you will implement the requiered methods but you can implement it in a UIView, UIViewController, MyOwnClass, MyOtherClass etc.
Thats why a property is normally declared like this
#property (nonatomic, weak) id <MyProtocol> delegate;
So you just say your delegate is an object which conform to the protocol.
You haven't shown enough code to give a completely definitive answer, but in general terms I would expect that the definition of your delegate is not just id, you've probably used NSObject* or something similar.
The compiler is doing "static" analysis of your source code and trying to determine whether or not the object specified by "self.delegate" might implement that method. If the data type is, say, NSObject*, then the compiler looks through that specific class definition to see if your method is present; if it isn't, then you'll get a warning.
If the data type of the message receiver is id, the compiler tends to give up and say "well, it could be anything so I'll assume this will work".
The result of the expression [obj dictionaryOfNames] is probably of type NSDictionary and the compiler can see that that particular class does respond to the count method.
Note, you can also get this problem if you have specified a class name for a property, but the compiler cannot see the entire class definition from this file. For example, if you have
myobject.h:
#class Something;
#interface MyObject
#property (retain) Something *delegate;
#end
myobject.m:
#import "myobject.h"
[self.delegate doItYouFool];
then the compiler can see that the result of the expression 'self.delegate' is of type Something* but it can not see the actual definition of that class and thus can't look through its supported messages. This usually results in a warning about 'forward definitions'. To fix it, you should import "something.h" into the .m file, so that the compiler has full knowledge about the classes it is working with. To just silence the warning, you cast to id
[(id)self.delegate doItYouFool];
You may, of course, also be getting warnings that 'doItYouFool' isn't a known method, again because you haven't included the header file that defines that message.

What's the most robust and readable way of ensuring objects conform to a interface/protocol in Objective C?

I'm trying code to an interface (or a protocol in Objective C terminology), not an implementation.
It's critical that we check objects conform to protocol before calling methods on them to prevent crashes.
Three Ways
In compiler
At runtime
Both
Best Solution... Surely Number 1?
I thought the best way would be in the compiler:
Warnings ahoy if you screw up
Eliminates conformsToProtocol:/respondsToSelector: boilerplate
At runtime it's too late if you made a mistake - the best you can do is not execute the code/show an error
But I see a lot of code that's doing it at runtime. Why?
Is it a readability issue - needing id <Protocol> everywhere?
My Question
What's the most robust and readable way of ensuring objects conform to a interface/protocol?
Code
1. Checking In Compiler
#interface ReportController : NSObject {
id <ReportGenerator> generator;
id <ReportSender> sender;
id report;
}
#implementation ReportController
-(id)initWithReportGenerator:(id <ReportGenerator>)generator_
reportSender:(id <ReportSender>)sender_ {
// Usual init stuff
generator = generator_;
sender = sender_;
return self;
}
-(void)generateAndSend {
report = [generator generate];
[sender sendReport:report];
}
#end
2. Checking At Runtime
#interface ReportController : NSObject {
id generator;
id sender;
id report;
}
#implementation ReportController
-(id)initWithReportGenerator:(id)generator_
reportSender:(id)sender_ {
// Usual init stuff
generator = generator_;
sender = sender_;
return self;
}
-(void)generateAndSend {
if ([generator conformsToProtocol:#protocol(ReportGenerator)] &&
[sender conformsToProtocol:#protocol(ReportSender)]) {
report = [generator generate];
[sender sendReport:report];
} else {
[NSException raise:NSInternalInconsistencyException format:#"Objects didn't respond to protocols..."];
}
}
#end
You should use both. Consider e.g.:
#protocol Proto
- (void)someFunction;
#end
#interface C : NSObject
- (void)proto:(id<Proto>)p;
#end
// ...
NSString *s = #"moo";
id i = s;
C *c = [[C alloc] init];
[c proto:s]; // warns
[c proto:i]; // doesn't warn
Objective-C and Cocoa are too dynamic to generally check such things at compile time (NSProxy standins, classes dynamically adding methods and protocols, ...).
It is nice to catch as many of such errors at compile-time as possible, but that alone is not sufficient.
As long as you don't use plain id as the type, the compiler will at least warn you if you make a mistake at compile time. So you should be fine with your code example #1.
Of course, sometimes you might be forced to work with an id object that you get from a subsystem that is not under your control. In such cases you can cast the object back to the type you think it has (e.g. id <ReportGenerator>), but you are usually better off if you perform a runtime check first. Better be safe than sorry...
On a final note: If your protocol has optional parts (declared with the #optional keyword), then for those parts you will obviously be able to do runtime checks only. The #required keyword mentioned by apurv is necessary only if you want to be explicit in your protocol declaration (a protocol's parts are required by default), or if you mix optional and required parts.
You should create methods with #required type in protocol.
So whatever class wants to take a contract with this protocol, will must have to implement those methods.
It will definitely make sure that the required methods are available at compile time only.