objective c class type checking - objective-c

I use MagicalRecord (which is a category for NSManagedObject) to fetch my models. Then I display them using a generic table view controller which display my data based on the Class name.
The problem is that when I want to fetch these data I use something like this:
Class type = NSClassFromString(modelName);
NSArray *model = [type MR_findAll];
This compile and run perfectly, excepts that xcode displays a nasty error: "No know class method for MR_findALL".
Can I do something like a "Class cast" to NSManagedObject so the error will disappear? I want to keep it dynamic, I don't want to define a condition for all my models.

The problem is not static type checking. Class, like id, disables static type checking.
The problem is something else: in Objective-C, in order to send a message using the [... ...] syntax, the compiler must have first seen the declaration of such a method somewhere. The reason that the compiler needs a declaration is that the compiler must compile a message dispatch to either a objc_msgSend or a objc_msgSend_stret call behind the scenes. Which one depends on the return type of the method. Therefore, it must know the types of the method (from a declaration) in order to compile a call to it.
Note, I said "somewhere", because that "somewhere" can be completely unrelated to where you use it. You could declare a dummy protocol that contains a declaration of the same message, that is never used anywhere, and it will serve the purpose of having a declaration. Or you can declare a category that contains it. Whatever. This might seem rather incredible to you. But if you take #HotLicks's solution with the protocol, and remove the cast, it will still not have the warning, because whether the protocol is used does not matter -- it's the declaration that is relevant here.

Define a protocol that includes MR_findAll. Cast your object to that protocol before making the call.
[(id<FindAllProtocol>)type MR_findAll]

Try to cast your type variable to id, which is a dynamic typing.

Related

Instance method not found warning, but working

In my implementation model, I need to have some differents files, with some similar methods. Also, I gather objects of differents types in a NSMutableArray, and try to call my similar method like this:
[[ArrayOfViews objectAtIndex:i] myMethodWithInt:'number' andExample:'char'];
But it gives me a warning:
Instance method '...' not found (return type defaults to 'id')
My code works, but should I fix this warning or not?
You should introduce the type:
MONType * obj = [ArrayOfViews objectAtIndex:i];
Then call the method using the typed variable:
[obj myMethodWithInt:<#number#> andExample:<#char#>];
It could be complaining for a number of reasons. Introducing the type will either fix those categories of issues, or at least give you a more useful warning. Specifically, the compiler does not see a declaration of the instance method -myMethodWithInt:andExample:.
You've likely either not imported a header, or declared the method so that it is visible in the translation.
My code works, but should I must fix this warning or not ?
Maybe. Sometimes it will 'work'. This is equivalent to C89's missing function prototypes, which were phased out because of all the issues they caused. So implicit parameters and return types can work if the parameters and return type is id compatible (including NSObject subclasses). But if it is not a match, then you should expect undefined behavior. You can avoid all this and introduce meaningful type checking by using the appropriate imports and declarations in your programs, and by introducing the type as illustrated above. This is useful because the compiler will save you from a ton of silly mistakes as you work and as your codebases evolve.
It's probably just a case of not having the method declared before the call. Try adding the method declaration to the interface section of the class.
Adding a typecast to your objectAtIndex call with get rid of the warning. This isn't a big issue, but it's good practice to typecast returns from an NSArray.
Basically:
YourObjectType *yourObject = (YourObjectType*)[ArrayOfViews objectAtIndex:i];
[yourObject myMethodWithInt:'number' andExample:'char'];
You need to add an #import statement for the header that declares the method or methods you want to call.

ObjC protocols potentially useless

In ObjC we can use protocols to restrict an id behavior, so we can declare something like
-(void)aMethod:(id<aProtocol>)aVar which works very well until we provide a value or a non-id variable as aVar, but this gets completely broken since we can pass a generic id variable delcared without protocols specifiers... Is this normal? Is there any workaround? Am I missing something?
Just use id less, and declare variables and parameters using the correct types, where possible. That is to say: don't pass ids around. If you are implementing a collections class (for example), then id's often useful.
My approach is to specify types, and introduce that type as local as possible in the source. So I omit id and add the type, and when (for instance) I take a reference from a collection, I create a variable:
MONType<MONProtocol>* thing = [array objectAtIndex:idx];
// now thing is correctly typed. use thing.
Similarly, if I have an id parameter, I declare a new variable:
- (IBAction)someAction:(id)sender
{
NSButton * button = sender;
// now use button, not sender
Protocols are extremely useful. Very often, better/cleaner than subclassing.
You're missing the understanding that types in Objective-C are determined at runtime, not compile time. Just because you say that an object will be of type id<aProtocol> does not mean that at runtime it is guaranteed to be so.
The idea of specifying something as id<aProtocol> is to aid you as a developer and people using your code. It aids you as a developer because the compiler will warn (or error under ARC) if you attempt to call a method on something that the compiler can determine it doesn't think exists on instances of its supposed type (excluding forwarding which could mean an instance responds to something the compiler cannot determine). It aids people using your code as it tells them the contract that they should adhere to when interfacing with your code.
So, in your question you say that:
but this gets completely broken if we pass a generic id variable delcared without protocols specifiers
Well, the compiler would warn and tell you that you're trying to pass something that does not conform to that protocol, except for the case of passing id. That's why you generally should try to type things more precisely than just id.
If you have a method defined like so:
- (void)aMethod:(id<aProtocol>)aVar
Then aVar could be of type SomeSubclass where that is defined like so:
#interface SomeSubclass : NSObject <aProtocol>
And you could then use aMethod like this:
SomeSubclass *obj = [SomeSubclass new];
[other aMethod:obj];
I (FINALLY) found out that using Objective-C++ is the way to go. Let's suppose I want to be able to pass NSString or NSNumber (instead of a too much generic id and instead of using protocols which become useless passing id values): well, I can create a C++ class having two distinct constructors, one for each ObjC class, so passing id values cannot be done anymore (almost directly). For example, let's take a look at
class NSStringOrNSNumber{
public:
NSStringOrNSNumber(NSString *);
NSStringOrNSNumber(NSNumber *);
};
The great advantage is that methods/functions taking a NSStringOrNSNumber parameter can get NSString/NSNumber values DIRECTLY, since the constructor acts as an implicit cast. In other words, if we have
void aFunction(NSStringOrNSNumber param);
the following calls are perfectly valid:
aFunction(#"Hello!");
aFunction(#25);
The only (little) downside is that we need the class to implement a function if we want to get back the value passed to the constructor.
Using a C++ class constructor to get something like id<NSCoding> is still better the using id<NSCoding> directly: in fact, if we do the following
#class classOne, classTwo;
class NSCodingClass{
private:
NSCodingClass(classOne *);
NSCodingClass(classTwo *);
public:
NSCodingClass(id<NSCoding>);
}
we won't be able to pass a generic id as a parameter (since it would be ambiguous: the compiler cannot know which constructor to call among the two private ones)

Objective C using multiple parameters across classes

I have the following declaration in my class called GameManager:
-(void)playBackgroundTrack:(NSString*)trackFileName isAmbient:(BOOL*)ambient {
}
I want to be able to call
[[GameManager sharedGameManager] playBackgroundTrack:BACKGROUND_TRACK isAmbient:NO];
or
[[GameManager sharedGameManager] playBackgroundTrack:BACKGROUND_TRACK isAmbient:YES];
but I am getting a warning:
Semantic Issue: Instance method '-playBackgroundTrack:isAmbient:' not found (return type defaults to 'id')
and trying to use the isAmbient parameter crashes.
it works ok if I leave the isAmbient declaration off in both places (but of course I can't use the parameter I want!).
You should use plain BOOL, not BOOL* in the declaration, like this:
-(void)playBackgroundTrack:(NSString*)trackFileName isAmbient:(BOOL)ambient;
Change the parameter type of isAmbient from BOOL* to BOOL.
The first piece of code is not a declaration, it's a definition. Declarations are signatures, end with a semicolon and are placed in header files (generally).
You need to then include that class header file with the declaration, so your other class knows the method exists. This will solve the "method not found" issue.
By the way, you probably meant to use a plain BOOL, not a pointer to it. This might be the cause of the crash.

Why do I need to cast before a method of an item of a NSArray can be called?

I am fairly new to Objective-C. Currently porting my own library from C#/Java to objective C.
I now run into a very strange problem for me.
I have a NSArray with several Note objects. I want to transpose on of these notes:
//Note.h
- (Note *) transpose: (int) semitones;
//Main
NSArray *notes = [get it from somewhere];
Note *transposedNote = [[notes objectAtIndex:0]transpose:1]; //Doesn't compile
Note *transposedNote = [(Note*)[notes objectAtIndex:0]transpose:1]//Does compile
Is this happening because there is already a transpose method available in the general libraries?
I thought due to the dynamic nature of objective-C at runtime it would be checked which class objectAtIndex returns and then sends the message to it?
It is my understanding that there is no runtime type checking for the assignment operator in Objective C. Since an array can contain a mixture of types, there is no way for the system to know what objectAtIndex returns.
How about
Note *transposedNote = [notes objectAtIndex:0]; // first line
[transposedNote transpose:1]; // second line
? Notice in the reference that objectAtIndex: returns an id, you will see it is pretty obvious:
In the code above, because id can fit into any object, the first line doesn't need to cast it into Note. In the second line I'm just calling a method on a Note so the compiler is happy.
In your code you are calling methods on the returned id object, so the compiler doesn't understand what you are trying to do. Just assign it to a Note reference and it will be fine.
Yes, the error is because there's already a transpose: method in AppKit. And you're also right that it normally doesn't cause an error when you have two unrelated classes implementing methods with the same name. The reason you get an error is because the two methods either return incompatible types or take incompatible types as arguments. In your particular case, you're seeing both problems:
-[NSResponder transpose:] takes an id and returns void
-[Note transpose:] takes an int and returns an id
These are totally incompatible types, and the compiler does need to know the types involved even if it doesn't know what exact method is going to be called.
It does compile unless you have -Werror set to treat warnings as errors.
It might produce a warning if the compiler doesn't already know about the selector or if the selector is declared in more than one class. In the former case, it should be necessary only to import the interface containing the selector. In the latter case, you'll need to do the cast to suppress the error.

Category before class

Just want to check: If you have a class that uses a method added to an existing objective-c object (i.e. NSArray) that you must define the category before the class that uses the category method. By accident I had done this the wrong way round and got the rather cryptic error ...
warning: cannot pass object of non-POD type 'void'
through variadic function; call will abort at runtime
Moving the category before my using class removed the error, a fairly simple case of define it before you use it I guess, but I just wanted to check.
many thanks
gary
As with everything in Objective-C (and plain C), things have to be declared before they are used. That means that if you want to use a function, class, category, struct or anything else in an implementation, you have to import the appropriate header file that declares it.
The order in which they are defined is irrelevant as long as the appropriate declarations are there.