Objective C using multiple parameters across classes - objective-c

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.

Related

Objective-C class method with generic parameter cannot be called in Swift

I have a method that is declared as follows in Objective-C
+(void) clearAll:(NSArray<ParameterType> *)values;
I have another class method that is declared as follows, in another object
+(NSArray *) values;
I am trying to call these from Swift as follows:
MyObject.clearAll(MyOtherObject.values())
It displays an array of different compile-time errors when I try to run it.
The errors are:
"Generic parameter 'ObjectType' could not be inferred"
then I try MyObject.clearAll(MyOtherObject.values() as NSArray<MyOtherObject>)
and get "Cannot specialize non-generic type 'NSArray'"
When I try MyObject.clearAll(MyOtherObject.values() as! Array)
I get "Array<MyOtherObject> is not convertible to [Any]"
How can I fix this?
The compiler errors are exceedingly unhelpful in this situation; they point pretty much everywhere except the right place. The problem is because of the generic parameter on MyObject: in Swift, an unspecialized generic type is not a thing unto itself, and methods can't be called on it. It has to be specialized to become concrete.
The resolution is simple: just add the type argument:
MyObject<MyOtherObject>.clearAll(MyOtherObject.values())
(You'll need to do the same for MyOtherObject if it's generic too.)

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.

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.

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.