Having just spent ages debugging this, I'm keen to understand what exactly is going on!
In a very contrived example, let's say we have two objects, Object1 has this method:
- (void) testMethod:(NSString *)testString
Object 2 has this method:
- (void) testMethod:(NSArray *)testArray
Then, back in Object1, there's the following code in a method:
NSArray *myArray = [[NSArray alloc] init];
[[[Object2 alloc] init] testMethod:myArray];
When I compile, Xcode gives a warning:
Incompatible pointer types sending 'NSArray *' to parameter of type 'NSString *'
I believe I'm right in saying the warning occurs because I never actually specify the type
of Object2. Explicitly casting the object to Object2 fixes it, but my questions are thus:
When calling testMethod on Object2, why is it using the method from Object1, when the objects are nothing to do with each other?
Why does the warning vanish if I move #import "Object2.h" into Object1.h instead of Object1.m?
Thank you!
When there are two methods with the same selector but different signatures, the compiler must decide which signature to use at compile time — because the signature can affect the code generated. Unfortunately, the compiler is deeply stupid, and the only way it has to tell which signature to use is by checking the static type of the receiver. In this case, both alloc and init return id, so the compiler has no information whatsoever with which it can decide what kind of object you're sending this message to. So it basically does this to break the tie: It closes its eyes, spins around in a circle a bunch of times, and whichever signature it's pointing to when it stops, that's the one it uses. Then it checks your argument type against this stochastic signature, and if it guessed wrong, it thinks you've passed the wrong type.
The best solution is to avoid signature collisions — descriptive method names usually take care of this on their own, and adding more specificity to one or both selectors is often a good way to solve the problem you're encountering (e.g. make it testMethodWithName:(NSString *)name).
It's also a good idea to statically type things as far as possible. Normally this isn't a problem, because you'll want to assign the newly created object to a variable anyway. In a pinch, when it would just be awkward to assign the result of the method to a variable, you can also just cast the ambiguous part to the correct type, like [(Object2 *)[[Object2 alloc] init] testMethod:myArray].
Related
I am going through some walkthroughs fpr Objective-C and I got to many places where I raised my eyebrows. I would love to get them down.
Is there a fundamental difference in message sending and method calling? Objective-C lets me do both: object.message yields the same result as [object message]. I think maybe nested messages cannot be created using the dot operator strategy?
I created an NSArray object, now I am about to print results for this using an NSEnumerator:
id myObject = [object objectEnumerator];
in a while loop iterating and printing results. The type of myObject is id, which means it's resolved at runtime and not compile time. I know very clearly what kind of objects are stored in my NSArray—they are NSStrings—so by changing the type of myObject to
NSString * myObject, it works just fine. However, I experimented and found out that myObject can be of any type, be it NSString or NSArray or NSEnumerator, and any of these work just fine, perfectly iterating the NSArray object and yielding the same results.
What's up with that?
I'm not sure what kind of distinction you're trying to make between "message sending" and "method calling", since they're two ways of describing the same thing. The dot syntax is just a shortcut for calling getters and setters, that is:
[foo length]
foo.length
are exactly the same, as are:
[foo setLength:5]
foo.length = 5
You should generally only use the dot syntax when you're using getters and setters; use the square bracket syntax for all of your other method calls.
For your second question: this is how dynamic typing works. Any type declarations you put in your code are hints to the compiler; your Objective-C method calls will always work as long as the objects respond to them.
It's a distinction oriented at the person reading your code. Dot syntax indicates state (I'm accessing an ivar), method syntax indicates behavior (I'm performing some action). To the runtime, both are the same.
I think Apple's intention is to show accessors as an implementation detail you shouldn't worry about. Even when they could trigger side effects (due to some additional code in the accessor), they usually don't, so the abstraction is imperfect but worth it (IMHO). Another downside of using dot notation is that you don't really know if there is a struct or a union behind it (which unlike message sending, never trigger side effects when being assigned). Maybe Apple should have used something different from a dot. *shrugs*
I think maybe nested messages cannot be created using the dot operator strategy?
Dot notation can be used to nest calls, but consider the following:
shu.phyl.we.spaj.da
[[[[[shu]phyl]we]spaj]da]
In this case, the uglier, the better. Both are a code smell because one object is creating dependencies to another object far far away, but if use brackets to pass messages you get that extra horrible syntax from the second line, which makes the code smell easier to notice. Again, convention is to use dots for properties and brackets for methods.
1: Your terminology is incorrect. The dot operator is not "method calling", a different operation. It's just a different visual appearance for message sending. There's no difference between [x y] and x.y. The dot syntax can only take one argument though, as it's intended to be used only for property access.
2: The static (compile-time) type of an object has no effect on its behavior at runtime. Your object is still an NSEnumerator even if you're calling it something else.
1) They're both message sending, just different syntax. [object message] is traditional syntax, object.message is the "dot notation", but means exactly the same thing. You can do some kinds of nesting with dot notation, but you can't do anything with methods that take complex arguments. In general, old hand Obj-C programmers don't use dot notation except for simple accessor calls. IMHO.
2) The runtime is really smart and can figure it out on the fly. The type casting of pointers is really just a clue to the compiler to let you know when you messed up. It doesn't mean a thing (in this case) when the message is sent to the array to fetch a value.
Message sending is the preferred way of doing this. It's what the community uses and reinforces the concept of objects sending messages to one another which comes into play later when you get into working with selectors and asking an object if it responds to a selector (message).
id is basically a pointer to anything. It takes some getting used to but it's the basis for how Objective-C handles dynamic typing of objects. When NSLog() comes across the %# format specifier, it sends a description message to the object that should should replace the token (This is implemented in the superclass NSObject and can be overridden in the subclass to get the desired output).
In the future when you're doing this, you might find it easier to do something like this instead:
for (NSString *s in YourNSArrayInstance) //assuming they are NSStrings as you described
{
NSLog(#"%#", s);
}
Or even simply just:
for (NSString *s in YourNSArrayInstance) //assuming they are NSStrings as you described
NSLog(#"%#", s);
You'll learn to like message sending eventually.
I have a CFDictionaryRef that doesn't retain/release its objects. When I add an item in it, I take care of retaining it, and later :
NSMutableArray *array = (__bridge_transfer NSMutableArray *)CFDictionaryGetValue(...)
[self performSelector:someSelector withObject:array];
Where someSelector is a variable holding a selector I know about. Now, that second line makes Xcode tell the notorious warning:
PerformSelector may cause a leak because its selector is unknown
Does that mean I should worry about ARC not knowing how to manage memory for the array variable of which I just transferred ownership?
From a comment to the accepted answer of this question, it appears that somebody at Apple has confirmed this hypothesis (citing the answer itself):
In fact, there are times when memory management is tied to the name of the method by a specific convention. Specifically, I am thinking of convenience constructors versus make methods; the former return by convention an autoreleased object; the latter a retained object. The convention is based on the names of the selector, so if the compiler does not know the selector, then it cannot enforce the proper memory management rule.
Thus, it has nothing to do with a possible leak of arguments passed to the performSelector: call, but rather to its returned value, for which Objective-C has no way of knowing if it was autoreleased or not. This is also what Martin R from the comments was assuming.
I'm disturbed by a weird behavior, illustrated by the following example:
NSMutableArray *a1 = [[NSMutableArray alloc] init]; // fine
NSMutableArray *a2 = [NSMutableArray array]; // fine, too
// compiler reports incompatible pointer types; good:
NSMutableArray *a3 = [[NSArray alloc] init];
// compiler says nothing and is happy to assign this!
NSMutableArray *a4 = [NSArray array];
Both init and array method of both the NSArray and NSMutableArray classes return id. However, the behavior when I call these methods is simply not the same, and clang lets me happily assign an empty NSArray to an NSMutableArray variable!
It turns out that clang will automatically change the return type of some methods, including the init family, to instancetype, and thus be able to determine at compile time that [[NSArray alloc] init] returns an NSArray * and not an NSMutableArray *. But this check simply doesn't work with the array method.
Why? Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?
Update
Good news: as of iOS 7, [NSArray array] returns instancetype, so the assignment to a4 above also yields a warning. Other methods like arrayWithContentsOfFile: or arrayWithContentsOfURL still return id, though…
But this check simply doesn't work with the array method. Why?
As the document you have linked describes, it is because -array does not yield a recognized Related Result Type. ObjC is very dynamic -- the compiler cannot guarantee the result type of +array. It does make that assumption with some methods because the naming conventions are well defined (e.g. +alloc, -init, +new, -self, etc.). So this implementation simply resorts to naming conventions.
The compiler also validates some naming conventions in areas you may not expect:
#implementation NSArray (DEMO)
- (id)initStr
{
return [NSString new]; // << warning. RE: init prefix
}
#end
Shouldn't lines like my last example generate at least a warning? Why aren't all these methods declared as returning instancetype? Will it change in the future?
instancetype was introduced about one year ago (from the looks of it). Some of the APIs were written decades ago. I suspect it will happen -- in time -- because (if used correctly) it can point out a lot of issues in existing code. Of course, those changes would break existing builds (again, typically good corrections if declared in the right places).
So file bugs and give the tools and libraries a few years to update. Assuming the changes are made, it will probably happen at a major OS update.
It would probably be best if it were enabled as an optional warning for some time (in the case of the system headers). Of course, they could still employ it with backwards compatibility for older compilers for new APIs.
Also, this change could be retrofitted quite easily (not that earlier compilers would make sense of the semantic difference between id and instancetype) by a simple typedef. One problem with a typedef is that it is a global declaration -- a compiler could restrict a word/modifier/attribute to a given scope, without causing all the pain of simulating a keyword by adding a global typedef. Apple's GCC may never support instancetype, so the logical way to introduce it for Apple's GCC may be a global typedef of id, which could cause problems for some people (with no semantic benefit, if that route were taken). Note that similar breaking changes have been made by Apple in the past.
As it turns out, you're not just allowed to use the wrong array type, you're allowed to use the wrong type of any object with a convenience initializer that returns id. For example, this compiles without a warning in sight:
NSMutableArray *a4 = [NSDictionary dictionary];
This is a side effect of using id to opt out of type safety, and as you note, it should be deprecated behavior and replaced with instancetype (which does throw an incompatible type warning when used in the manner above).
Unfortunately, it's not a bug. instancetype being a fairly new keyword, it's adoption is not widespread yet, and it would be a bold move to start using it throughout Apple's frameworks. You never know, there's always hope for the next SDK!
I am just starting climbing the Objective C learning curve (using Nerd Ranch iOS programming book).
Based on what I have know from other languages about "nesting" multiple executions within one line I assumed that I can alter:
NSString* descriptionString = [[NSString alloc] initWithFormat:#"%#", possesionName]
with a two line version:
NSString* descriptionString = [NSString alloc];
[descriptionString initWithFormat:#"%#", possesionName]
but it seems that the second attempt raises an exception
2012-01-22 18:25:09.753 RandomPossessions[4183:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -length only defined for abstract class. Define -[NSPlaceholderString length]!'
Could someone help me understand what exactly I am doing wrong here? Thanks a lot in advance.
PS. If this is a way Objective C messages work and you have to make alloc and init in one line just let me know - I assumed this is just a set of functions that either can be executed two in one go or one after another.
An important difference between both versions (they are not exactly equal) is that in the first version you use the result of initWithFormat for the variable descriptionString, while you use the result of alloc in the second. If you change your code to
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName]
all should be well again. It is specified that an object returned by alloc shall not be seen as initialized and functional until some init Method has been called and init might return something else.
The alloc method will allocate memory for a new object. But the init method might throw away that memory and return a completely different object. Or it might return nil. This is why you must always do self = [super init] when you override an init method.
NSString is one class that does this kind of thing all the time.
I'm not exactly sure why the exception is happening, but I believe it could be ARC injecting code in between your two lines of code or something similar. Whatever it is, something is trying to act on the allocated object that has never been initialised, and this is a huge problem that can lead to all kinds of issues. Consider yourself lucky it threw an exception, sometimes it wont.
The NSString class might not actually be a real class. It may contain almost no methods and almost no variables. All it has is a bunch of factory methods to create "real" string objects of some other class, and this is done using methods like initWithFormat:. So, by long standing convention alloc/init must always be done in a single statement and there are a handful of places where, usually for performance reasons, something will rely on this convention being used.
Basically, objective-c is a language where you don't need to know exactly what is going on inside an object. You just need to know what messages can be sent to an object, and how it will respond. Anything else is undefined behaviour and even if you learn how it works, it is subject to change without notice. Sometimes the behaviour will change depending on circumstances that are completely illogical, for example you might expect the "copy" method to give you a copy of the object you send it to, and while this is the default behaviour, there are many cases where it will actually just return the same object with slightly different memory management flags. This is because the internal logic of the class knows that returning the same object is much faster and effectively identical to returning an actual copy.
My understanding is copy sent to NSString may return a new object, or it may return itself. It depends on which NSString subclass is actually being used, and there isn't even any documentation for what subclasses exist, let alone how they're implemented. All you need to know, is that copy will return a pointer to an object that is perfectly safe to treat as if it was a copy even though it might not be.
In a "proper" object oriented language like Objective-C, objects are "black boxes" which can intelligently change their internal behaviour at any time for any reason, but their external behaviour always remains the same.
With regard to avoiding nesting... The coding style for Objective-C often does require extensive nesting, or else you'll be writing 10 lines of code when only 1 is really needed. The square brace syntax is particularly suited to nesting without making your code messy.
As a rule of thumb, I turn on Xcode's "Page Guide at column" feature, and set it to 120 characters. If the line of code exceeds that width then I'll think about breaking it into multiple lines. But often it's cleaner to have a really long line than three short lines.
Be pragmatic about it. :)
From Apple's library reference, initWithFormat:
Returns an NSString object initialized by converting given data into Unicode characters using a given encoding.
So you can use these two lines of code:
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName];
For more info please go to:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/initWithFormat:
What is better and why ?
What is better in such situations as the class init method and usual variables in a code ?
What is better and why ?
Explicit typing information is always better unless you just can't use it for some reason (see below).
It allows the compiler to much more stringently validate the code and will catch many errors at compile time that would otherwise cause your app to crash at runtime.
A long, long, time ago, everything in the APIs used id. This proved to be a complete pain in the butt; fragile and led to many crashes that would have been caught with specific types.
So, it was changed. (This was ~1994).
What is better in such situations as
the class init method and usual
variables in a code ?
For init, you have no choice but to use the generic (id) return type. Objective-C does not support either co-variant or contra-variant declarations, nor is there a mechanism for generalizing the declaration of init while also providing support for specific type checking.
Same goes for retain, objectAtIndex:, addObject: and many other methods that take or return one of many kinds of objects (or take 'em as arguments).
And, no, there is absolutely no performance difference whatsoever between id and, say, NSView*.
can you give an example when explicit
typing will cause a problem please?
If you wrote:
- (MyClass *) init;
And in a subclass:
- (MySubclass *) init;
You'd get compiler warnings out the wazoo most likely or you'd have to typecast out the wazoo.
On recent versions of clang (in Lion) you should actually not return id, and instead return instancetype. This is a keyword that is used in return types to specify that the type it returns is an instance of the class receiving the message. It is now the preferred return type for init methods on OS X Lion.
Explicit typing provides build-time protection, informing you of likely problems if you do things such as casting or performing operations on something that probably won't work.
Explicit typing also helps prevent non-obvious transfer of mistyped objects, something traveling through a path in your code you hadn't considered that turns out to be of an unexpected type. This kind of bug often doesn't become clear until the program has been tested a lot, more commonly after its release.
It's also helpful for future programmers (including your future self) trying to work with your code, making to more likely that they'll be able to tell at glance what an object is supposed to be. It makes code more "self-documenting" as a result.
Some things cannot have a meaningful type because no type actually applies. Other times you need to use id because you need to be able to accept any type of object. Cocoa Touch uses it, for example, when referring to the sender of a message because anything could have sent it; specifying an explicit type simply wouldn't work.
The vast majority of the time, though, an explicit type is to your advantage.
Use a type as specific as you can but not more so. Consider how you are using any particular variable, argument, or return type and set its type appropriately.
For example a UITableView's dataSource property is declared as id<UITableViewDataSource> because the table view only cares that its data source is some object which conforms to the UITableViewDataSource protocol. This allows the data source to be flexible enough for use with any specific class which implements the protocol but still allows the compiler to warn you if you attempt to assign an object which does not implement that protocol as the data source.
If you are too specific then your code becomes inflexible, accepting only specific implementations which are not strictly necessary (ie demanding a NSMutableString when you could really work with any NSString).
If you are too vague (typing everything as id for example) then you lose the ability to identify when you are sending unrecognized selectors to a particular instance and the compiler cannot identify any number of invalid statements.
For init methods follow the advice in The Objective-C Programming Language
The return type of an initializer method should be id.
The reason for this is that id gives an indication that the class is purposefully not considered—that the class is unspecified and subject to change, depending on context of invocation. For example, NSString provides a method initWithFormat:. When sent to an instance of NSMutableString (a subclass of NSString), however, the message returns an instance of NSMutableString, not NSString. (See also, though, the singleton example given in “Combining Allocation and Initialization.”)
I don't think there is a performance difference between both.
You can let id return type for init because you can cast the result of your init.
For exemple :
Toto *foo = [[Toto alloc] init];
id foo2 = [[Toto alloc] init];
Both work but you'll have to cast foo2 variable like that (Toto *)foo in order to access property or methods of your instance without creating a compiler warning. Even if it works fine...
I think some developers let id because they just pass there variable trough instance and don't use it. That kind of use allow to not import the .h
Regards,
KL94