Category before class - objective-c

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.

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 class type checking

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.

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.

Calling parent class within class method gives me "matching method signature" warning

I have a class that has two classes A and B added to it. In a method in class A, I am trying to call a class B method
Let's assume that the parent class is debugZoneScene, debugZoneLayer is class A and tetraCounter is class B.
Here is a method from debugZoneLayer (class A):
-(void) getHeroVel {
DebugZoneScene *debugZoneScene = (DebugZoneScene*)self.parent;
[debugZoneScene.tetraCounter setTetras];
}
It calls the method, but I get the warning:
'-[DebugZoneLayer getHeroVel]':
'CCNode' may not respond to '-setTetras' (Messages without a matching method signature will be assumed to return 'id' and accept '...' as arguments.)
I've tried Googling this, but I couldn't really find anything that related exactly to my problem. I am using Cocos2D, but I think this problem doesn't have anything to do directly with that, and can still be resolved having knowledge in Objective C. Any ideas?
The compiler is telling you that it thinks that debugZoneScene.tetraCounter is an object of type CCNode, not whatever your ClassB is. Check how tetraCounter is declared and allocated in DebugZoneScene.
You can make the warning go away by casting:
[(ClassB *)(debugZoneScene.tetraCounter) setTetras];
this tells the compiler that you don't care what it thinks and you're sure that the object is ClassB. This doesn't solve the actual problem, however.
Your pseudo really fits you... without more details about the signature of setTetras, it will be quite difficult to guess what is wrong in your code ^^
Anyway did you #import the header for TatraCounter class declaration, so that the file where you wrote this code knows about the methods available (and their signature) for the TetraCounter objects?