Can you send a message that an object won't catch? - objective-c

I'm reading this book and it says that this code will compile but cause a runtime exception. But when I run it on my computer it doesn't compile: "No known instance method..."
#import "Fraction.h"
int main (int argc, char * argv[])
{
#autoreleasepool {
Fraction *f = [[Fraction alloc] init];
[f noSuchMethod];
}
return 0;
}
My question is when does doing this cause a runtime exception and when does it cause a compile-time warning?

The compile-time error is because the selector noSuchMethod does not appear on any class the compiler has seen. Under ARC, the compiler cannot safely decide how to apply memory management rules in that case (it could try to follow standard naming conventions, but that could lead to extremely difficult bugs if the selector were declared with alternate semantics in some other header). It is also almost certain a mistake, so the compiler rightfully balks at guessing. (There is another issue related to the return type, and before ARC the compiler would guess about that, but it generally wasn't what you wanted even if it happen to guess right most of the time.)
If -noSuchMethod were known to exist on some class, but not Fraction, then the compiler would issue a warning. At runtime, if f failed to respond, you'd get a runtime exception. (This was the old behavior; it seems that clang may have gotten a bit more strict in my experiments and will throw an error now in this case, too. This may be the confusion with your book if it was written before the change, or the author has not rechecked it in a while.)
If noSuchMethod were known to exist, but f were declared of type id, then no warning would be generated. At runtime, if f failed to respond, you'd get a runtime exception.

It depends ...
When you use ARC then, per default, the compiler will throw an error for a method call when the method name is not known at compile time.
When you don't ARC then you get a warning only at compile time.
You can, however, customize the warning/error level of the compiler to get is passed in both cases.
Objective-C binds method calls during runtime. If you manage to get it compiled then you could even react on the runtime error right before the exception is thrown and dynamcially register a method during runtime under the name of noSuchMethod and actually perform some code when ever the method is invoked in the future during the same runtime session. It is probably this behaviour of dynamic binding during runtime that the autor of your book is trying to explain by example.

Related

Why would compiler give an error if a method get called cannot be found within the given glass since Objective-C checks it during runtime? [duplicate]

I heard that Objective-C is influenced by the "message passing mechanism" of SmallTalk.
Objective-C, like Smalltalk, can use dynamic typing: an object can be
sent a message that is not specified in its interface. This can allow
for increased flexibility, as it allows an object to "capture" a
message and send the message to a different object that can respond to
the message appropriately, or likewise send the message on to another
object.
And I felt for codes like [anObject someMethod], the binding of someMethod to the machine code may happen at run-time..
Therefore, I write a demo like this:
#import <Foundation/Foundation.h>
#interface Person : NSObject {
#private char *name;
}
#property (readwrite, assign) char *name;
- (void)sayHello;
#end
#implementation Person
#synthesize name;
- (void)sayHello {
printf("Hello, my name is %s!\n", [self name]);
}
#end
int main() {
Person *brad = [Person new];
brad.name = "Brad Cox";
[brad sayHello];
[brad sayHelloTest];
}
I tried [brad sayHelloTest] to send brad a message sayHelloTest which brad doesn't know how to handle with.. I expect the error will NOT happen at compile-time..
However, the compiler still throws an error:
main.m:24:11: error: instance method '-sayHelloTest' not found (return type defaults to 'id') [-Werror,-Wobjc-method-access]
[brad sayHelloTest];
^~~~~~~~~~~~
main.m:3:12: note: receiver is instance of class declared here
#interface Person : NSObject {
^
Change [(id)brad sayHelloTest] to [(id)brad sayHelloTest]; doesn't work either.. (The compiling command is clang -Wall -Werror -g -v main.m -lobjc -framework Foundation -o main)
In Objective-C, does the binding of method really happen at "run-time"? If so, why will there be a compiler error like this?
If the binding doesn't happen at "run-time", why was "Objective-C" called "dynamic typing language"?
Does anyone have any ideas about this?
One job of a compiler is to catch as many errors at compile time as possible. If it can tell that the call will fail at runtime, you generally want it to complain.
You can suppress this via casting to show that runtime resolution is happening:
[(id)brad sayHelloTest];
Because the IDE can infer the obvious error from the context.
When you write if (a = 1),you will get a warning. A good IDE should help you find mistakes as early as possible.
I figured out the reason finally..
It throw errors during compiling because -Werror flag is included, which will turn warning into error..
http://clang.llvm.org/docs/UsersManual.html#cmdoption-Werror
After I delete -Werror flag, everything works as expected and the error only happens at run-time.
It has become a compiler error only within the last five years for there to be no known declaration of a method. It has to do with Automatic Reference Counting. Under ARC, the compiler is now responsible for the reference-counting-based memory management that Cocoa uses.
Given that responsibilty, it must be able to see the declarations of methods for any messages before they are sent, so that it knows what retains and releases are appropriate.
The method resolution (the lookup of the method on the class) does still happen at runtime, and -- particularly if you disable ARC -- you can still take advantage of message forwarding.
One way around ARC's requirement was given by Marcelo Cantos -- cast the receiver to id. Another is to use performSelector:. A third -- though I can't recommend it -- is to use objc_msgSend() directly.
Note that the "binding" of the method does, and always did, happen at compile time. Methods are associated with classes, when the classes are defined. Messages are distinct from methods, and it is they that resolve at runtime to a method.

Objective-C ARC and longjmp

What is the best practice for mixing Objective-C ARC with longjmp?
I am using Lua as scripting language, and my platform exports custom library for scripts. Entry points do check arguments with luaL_checkinteger(L, 2) (among others), which, in turn, may call luaL_typerror(L, 2, ...), that is implemented in Lua with setjmp/longjmp. As far as I know, ARC simply auto-generates retain/release code, but what happens if it longjmps out of scope? Will this code leak on mistyped arguments?
static int
set_tag(lua_State *L)
{
NSControl *control = (__bridge NSControl *)lua_topointer(L, 1);
[control setTag:(NSInteger)luaL_checkinteger(L, 2)]; // may longjmp!
return 0;
}
In the snippet above, control will be temporarily retained by ARC, but with longjmps uncatchable nature, corresponding release call may never happen. On the other hand, all arguments may be checked before assigning to control variable.
static int
set_tag(lua_State *L)
{
NSInteger tag = luaL_checkinteger(L, 2); // may longjmp!
NSControl *control = (__bridge NSControl *)lua_topointer(L, 1);
[control setTag:tag];
return 0;
}
Does it resolve [potential] leak above? Are there better ways to do this?
UPDATE: longjmp only unwinds to Lua internals, and never crosses any system code, except for Lua source (which is aware), and my entry points (which I hope are aware).
I'm pretty sure that second snippet does right, but I need kind of formal proof.
LATE UPDATE:
LuaJIT implements dwarf2-compatible errors, so they are just like C++ exceptions. Pass -fobjc-arc-exceptions compiler flag to arc-enabled sources with Lua code and any retained object will be released on any lua_error. Nothing to worry about now! You are still not allowed to throw errors across Cocoa runtime, though.
I recall that original Lua may be compiled with exceptions too, but I'm not sure.
Doesn't really matter if ARC is in use or not; any setjmp/longjmp that jumps over any frame of code from the system frameworks will yield undefined behavior (for the same reason that exceptions cannot be used for recoverable error handling).
So, yes, that code might leak. Might because it depends on whether the compiler emits a retain/release in that block and where. Might also because whether the compiler emits retain/release will be impacted by the optimization level and, over time, the version of the compiler.
longjmp only unwinds to Lua internals, and never crosses any system
code, except for Lua source (which is aware), and my entry points
(which I hope are aware).
That is helpful. As long as you structure your entry points such that they never intermingle system scope with Lua jumpable scopes, you should be OK. I would recommend turning off ARC in the source files where you have to manage this (and, of course, put the ObjC->Lua interface into a nicely encapsulated bit of implementation so the rest of your code can be ARC clean).
Consider, though, that there is non-obvious risk:
for(id x in anArray) {
... lua call that causes longjmp ...
}
The above would cause lua to "jump over" system code. Same goes for enumerateWithBlock:, KVO, Notification handlers, etc...
You're going to have to think very very carefully about every potential stack trigger by a call from Lua into your code. If that call triggers any kind of automated behavior on the part of the system that could then call Lua API that could trigger a longjmp, all bets are off.
longjmp() may cause crashes or leaks in ARC. Arranging the code so longjmp() and ARC don't interfere is difficult.
If the longjmp() is only for a fatal error path and you expect to halt the process in response then you may be able to ignore the problem. This is what ARC does with C++/ObjC exceptions by default. ARC code is expected to leak when exceptions are thrown. There's a compiler option to enable the clean up code for exceptions, but that hurts performance.
If the longjmp() is not a process-killing error then your best option is to turn off ARC in any code that may be skipped by a longjmp() call.

"Initializer is not a compile time constant" error works in .mm?

I'm puzzled by a situation I'm observing and would love some insight. First, I'm using Xcode 5, with LLVM 5 compiler options set to defaults.
I have a line in a .m file such as:
static NSArray * const kSchemaVersions = #[#"1"];
And, as expected, I see a compiler error saying Initializer element is not a compile-time constant.
However, if I place this same line in a .mm (Objective C++) file, the compiler does not complain.
I completely understand why it shouldn't work, but I'm baffled as to why it appears to.
Thoughts?
As you mentioned, in C and Objective-C static variables can only be initialised with compile-time constants. In C++ (and therefore Objective-C++) on the other hand, static variables are assigned at run time, before main runs.
For more details have a look at Eli Bendersky's Non-constant global initialization in C and C++

Proxies and ARC, incompatible?

I'm trying to implement proxies for database stored procedures. The idea is to have my code find a series of stored procedures in the database, then create a proxy object in memory which will implement methods matching those stored procedures. That proxy object could be derived from NSProxy or NSObject, both should work. The basic idea is that any method calls made to my proxy object would go to resolveInstanceMethod: and be handled there if the method call matched a stored procedure in the database.
But before even getting close to my objective, I'm stumped by ARC seemingly not allowing me to do method calls to undeclared methods. Back in the days before ARC, we got a warning like "object may not respond to selector" from the compiler and that was that, but with ARC enabled, I'm getting a compiler error saying "no known instance method for selector...". This also happens if I first cast the proxy object to (id).
This all seems to imply that ARC is incompatible with runtime discovery of methods, which is one of the fundamental characteristics of objective-c. It seems to mandate that all methods used must be declared in interfaces before compilation, just as in C++.
Or, more likely, I'm missing something essential. If so, what?
Edited to include code:
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
MyTargetClass *mtc = [[MyTargetClass alloc] init];
[mtc doSomething];
}
return 0;
}
MyTargetClass contains nothing:
#interface MyTargetClass : NSObject
#end
The [mtc doSomething] call elicits a warning if in a non-ARC project, but elicits a compiler error with ARC. IMHO that means I can't add methods to a class in runtime if ARC is used.
mwehlou,
First, if you don't show us any code, then it is really hard to help you.
Second, if you know that you will provide the instance method, then you can silence the warnings with the following #pragmas:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[Your Selector Based Code Here]
#pragma clang diagnostic pop
If that is the warning symbol, well, you can probably find it from within your build settings in Xcode.
Andrew

Did the Target-Action design pattern became bad practice under ARC?

For years I've been following a great pattern called Target-Action which goes like this:
An object calls a specified selector on a specified target object when the time comes to call. This is very useful in lots of different cases where you need a simple callback to an arbitrary method.
Here's an example:
- (void)itemLoaded {
[specifiedReceiver performSelector:specifiedSelector];
}
Under ARC it now turns out that doing something like this all of a sudden became dangerous.
Xcode throws a warning that goes like this:
PerformSelector may cause a leak because its selector is unknown
Of course the selector is unknown since as part of the Target-Action design pattern you can specify whatever selector you want in order to get a call when something interesting happens.
What bugs me most about this warning is that it says there can be a potential memory leak. From my understanding ARC doesn't bend the memory management rules but instead simply automates the insertion of retain/release/autorelease messages at the right locations.
Another thing to note here: -performSelector: does have an id return value. ARC analyzes method signatures to figure out through application of naming conventions if the method returns a +1 retain count object or not. In this case ARC doesn't know if the selector is a -newFooBar factory or simply calling an unsuspicious worker method (which is almost always the case with Target-Action anyways). Actually ARC should have recognized that I don't expect a return value, and therefore forget about any potential +1 retain counted return value. Looking at it from that point of view I can see where ARC is coming from, but still there is too much uncertainty about what this really means in practice.
Does that now mean under ARC something can go wrong which would never happen without ARC? I don't see how this could produce a memory leak. Can someone give examples of situations in which this is dangerous to do, and how exactly a leak is created in that case?
I really googled the hell out of the internet but didn't find any site explaining why.
The problem with performSelector is that ARC doesn't know what the selector which will performed, does. Consider the following:
id anotherObject1 = [someObject performSelector:#selector(copy)];
id anotherObject2 = [someObject performSelector:#selector(giveMeAnotherNonRetainedObject)];
Now, how can ARC know that the first returns an object with a retain count of 1 but the second returns an object which is autoreleased? (I'm just defining a method called giveMeAnotherNonRetainedObject here which returns something autoreleased). If it didn't add in any releases then anotherObject1 would leak here.
Obviously in my example the selectors to be performed are actually known, but imagine that they were chosen at run time. ARC really could not do its job of putting in the right number of retains or releases here because it simply doesn't know what the selector is going to do. You're right that ARC is not bending any rules and it's just adding in the correct memory management calls for you, but that's precisely the thing it can't do here.
You're right that the fact you're ignoring the return value means that it's going to be OK, but in general ARC is just being picky and warning. But I guess that's why it's a warning and not an error.
Edit:
If you're really sure your code is ok, you could just hide the warning like so:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop
The warning should read like this:
PerformSelector may cause a leak because its selector is unknown. ARC doesn't know if the returned id has a +1 retain count or not, and therefore can't properly manage the memory of the returned object.
Unfortunately, it's just the first sentence.
Now the solution:
If you receive a return value from a -performSelector method, you can't do anything about the warning in code, except ignoring it.
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
Your best bet is this:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
#pragma clang diagnostic pop
Same goes for the case in my initial question, where I completely ignore the return value. ARC should be intelligent enough to see that I don't care about the returned id, and therefore the anonymous selector is almost guaranteed not to be a factory, convenience constructor or whatsoever. Unfortunately ARC is not, so the same rule applies. Ignore the warning.
It can also be done for the whole project by setting the -Wno-arc-performSelector-leaks compiler flag under "Other Warning Flags" in project build settings.
Alternatively, you can surpress the warning on a per-file basis when you add that flag under Your Target > "Build Phases" > "Compile Sources" on the right-hand side next to the desired file.
All three solutions are very messy IMHO so I hope someone comes up with a better one.
As described above you get that warning because the compiler does not know where (or if) to put the retain/release of the performSelector: return value.
But note that if you use [someObject performSelector:#selector(selectorName)] it will not generate warnings (at least in Xcode 4.5 with llvm 4.1) because the exact selector is easy to be determined (you set it explicitly) and that's why compiler is able to put the retain/releases in the correct place.
That's why you will get warning only if you pass the selector using SEL pointer because in that case the compiler is unable to determine in all case what to do. So using the following
SEL s = nil;
if(condition1) SEL = #selector(sel1)
else SEL = #selector(sel2)
[self performSelector:s];
will generate warning. But refactoring it to be:
if(condition1) [self performSelector:#selector(sel1)]
else [self performSelector:#selector(sel2)]
will not generate any warnings
ARC is throwing the warning because it can't guarantee that the selector isn't creating an object it doesn't know about. You could theoretically receive something from that method that ARC can't handle:
id objectA = [someObject performSelector:#selector(createObjectA)];
Maybe someday it can, but right now it can't. (Note if it does know the object (it's not an id) it doesn't throw this warning).
If you're trying to simply execute a method without receiving an object back from it, I recommend using objc_msgSend. But you've gotta include in your class:
#include <objc/message.h>
objc_msgSend(someObject, action);