Objective-C dynamically created methods and compiler warnings - objective-c

If I generate methods dynamically on runtime and then call them - how can I convince compiler that the class will respond to undeclared (generated) methods and make it not throw warnings?
Update in regard to answers
When I generate the methods - their name is not known at compile time. To give an example - if I have a view controller MyFooController and it's initiated with method initWithFoo:(Foo*)foo, I'd be able to generate method like pushMyFooControllerWithFoo:(Foo *)foo for UINavigationController. Hence you notice that declaring such methods would be counter-productive.

This doesn't directly answer your question, but if I was generating method names (presumably from strings), I would call them using the string names, hence bypassing the compiler warnings.
[fooController performSelector:NSSelectorFromString(#"pushMyFooControllerWithFoo:") withObject:foo];
That way you are responsible for the validity of the generated method names.

Since you are adding methods on runtime, so you should also invoke them with runtime function, objc_msgSend or performSelector:withObject:for example, so the compiler will not going to warn you anything.

Well, if you call them, you know their signature, and if you know their signature, you can declare them, can't you?

Declare this method in a category for NSObject and make an empty implementation:
#interface NSObject (DynamicMethodsCategory)
- (void)doSomething;
#end
#implementation NSObject (DynamicMethodsCategory)
- (void)doSomething
{
}
#end
In your object you can call it without any warnings:
#implementation MyObject
- (void)someMethod
{
[self doSomething];
}
#end
Then generate implementation of [MyObject doSomething] dynamically, it will be called instead of NSObject's one.
Update:
Alternatively, the method can be declared in a category for the object. This suppresses the compiler's Incomplete implementation warning. However, I think this is not a good workaround, because the application will crash if the method is not created dynamically in runtime before it is called.

Related

Passing a Class which conforms to a protocol as an argument

Is there a way in Obj-C to pass a class as a parameter which conforms to a protocol?
For example, say I have a slew of classes which conform to this protocol:
#protocol IMyProtocol <NSObject>
+ (id)someMethodINeed;
#end
I want to set up a method which takes in any class which conforms to this protocol.
#interface SomeClass : NSObject <IMyProtocol>
#interface AnotherClass : NSObject<IMyProtocol>
I want to make a method like this:
- (void)doSomething:(Class<IMyProtocol>)customClass
{
[customClass someMethodINeed];
//A whole bunch of other code I don't want to copy/paste each time
}
And call it with any of those classes
[self doSomething:SomeClass];
[self doSomething:AnotherClass];
I can't figure out the syntax though. Is this possible?
To make your code work you just need to pass the class, change your two calls to:
[self doSomething:SomeClass.class];
[self doSomething:AnotherClass.class];
However while this works the compiler is not doing the checks you probably expect. You can pass any class to doSomething: regardless of whether it implements your protocol; and [customClass someMethodINeed] is a dynamic call resolved (or not) at runtime and may produce an "unrecognized selector sent to class" error.
(Objective-C always does dynamic method lookup but with typed values the compiler is usually able to determine at compile time whether the runtime resolution will succeed or not. What you are are seeing here is the behavour you get when you program using id.)

Objective-C "Override" Specifier

In Java, C++11 and some other languages you can specify that a method is intended to override another method from a base class or interface, if you then at a later point remove the method from the base class you get a compiler error. I use protocols with optional methods a lot and find that if I remove a method from there I have a tendency to forget to remove the code that implemented the method. This does not generate an error or a warning, thus creating a "dead" method.
Consider:
#protocol Prot <NSObject>
#optional
- (void)bar;
- (void)tempBar;
#end
#interface MyType : NSObject <Prot>
#end
#implementation MyType
- (void)bar { /**/ }
- (void)tempBar { /**/ }
#end
If I at one point remove tempBar from the protocol, I would like to get at least a warning from tempBar being implemented in MyType.
Is there any way in Objective-C to specify that a method is expected to be an implementation of a protocol method?
Objective-C is a dynamic language and this is rather impossible to enforce at compile time. Note that in Obj-C you can actually call methods there are not even there and the app won't crash (well, the default implementation will raise an exception but you can change that behavior).
The method can be also added in an extension, or added at runtime. Or it is just not present in the header.
Note there is also the opposite problem. When subclassing, you can override a method which you don't even know is there, because it is not present in the headers.
This is one of the reasons why Apple is moving to a more predictable language, that is, Swift.

Forcing a superclass to call its own method when sender subclass has the same signature

Let's say I have this class and its subclass
#interface MySuperClass
- (void)open:(id)type value:(id)value;
- (void)openWebpage:(NSURL*)url;
#end
#interface MySubClass
- (void)openWebpage:(MyBookmarkClass*)bookmark;
#end
and that calling [someMySubclass openWebpage:someBookmark] calls [super open:BookmarkClass value:self.url]. And open:value calls [self openWebpage:url].
I realize this is very contrived, but I ran into a similar situation. My confusion is that even though [self openWebpage:url] is being called in MySuperClass, when it gets executed openWebpage: is being run in the context of the caller, MySubClass, which doesn't know what to do with an NSURL.
So my question is: is there any way to force something to be called in its original context? Or make it as though it calls super as many time as it can up the chain and find the method closest to the top?
There is only one context. There's a single object. Its class is MySubClass.
It is a mistake to have overridden the method with a different incompatible type. Don't do that. This is not C++ with function overloading. There's no dispatch based on the type of arguments.
The convention is to name methods by what they're acting on. So, you may have a method named -openWebpageURL: in MySuperClass and another method introduced in MySubClass named -openWebpageBookmark:. Note that MySubClass would still have a method named -openWebpageURL: inherited from MySuperClass.

Objective-C: Should init methods be declared in .h?

First of all, as I understand it, init in Objective-C, functionally is similar to a constructor in Java, as it is used to initialize instance variables and prepare a class to do some work. Is this correct?
I understand that NSObject implements init and as such it does not need to be declared in any .h files.
But how about custom implementation of init for a given class, for example:
(id) initWithName:(NSString *) name
Should declaration like this be listed as part of .h, or it is not necessary? Is it done by convention or is there any other reasoning?
init is by no means similar to constructor in Java/C++. The constructor always executes when the object is created. But the execution of init is up to you. If you don't send init message after alloc then it will not execute.
// init does not execute here
MyObject *obj = [MyObject alloc];
And this will work without any problems if you derive from NSObject, as init of NSObject does nothing.
You do not need to add init in the header file, because it is inherited from NSObject but you need to add custom init methods (that are not inherited) to the header file. Note that init methods are just normal methods with a naming convention, but technically there is no difference from other methods.
If you do not specify your custom init methods in the header file, but send that message to an object, the compiler will generate a warning. There will be no compile error. So if you decide to ignore the warning then you can omit that from header too. But you will get a runtime crash if the method is not actually implemented. So it's better to add all methods that are not inherited in header file.
Yes you have to declare it if you want to be able to call this personalized initialisation method (initWithName). And the first think you have to do in that method is to call [super init];.

Delegate method seems to be #required where it's declared as #optional

I've created my own Delegate for a ObjC class. The class itself deals with Core Data operations. Delegate methods are used to inform other classes about changes that happened to the datastore. The class that deals with the datastore is called Datastore and it's delegate is called DatastoreDelegate. A UIViewController of mine (ContactsViewController) implements the delegate.
My DatastoreDelegate is declared as followed:
#class Datastore;
#protocol DatastoreDelegate <NSObject>;
#optional
- (void)didAddMessage:(Message *)message;
- (void)didUpdateContact:(Contact *)contact;
- (void)didAddContact:(Contact *)contact;
- (void)didUpdateContact:(Contact *)contact;
- (void)didDeleteContacts;
#end
The weird thing is, my code all worked fine with these methods except for the [didAddMessage:] method. Whenever I try to call this delegate from within the Datastore class I get an error from the ContactsViewController. The error tells me the [didAddMessage:] selector is missing in the ContactsViewController instance (Unrecognized selector sent to instance). How can the selector be missing if it's optional?
I should note that my Datastore class is a Singleton. I'm not sure if this is somehow related to this issue I'm having.
"Optional" means the caller is responsible for checking that a target responds to a given selector. E.g.:
if ([_myDelegate respondsToSelector:#selector(didAddMessage:)])
{
[_myDelegate didAddMessage:theMessage];
}
Did you implement didAddMessage: in your ContactsViewController? It's optional so you aren't forced to implement it, but if you send the didAddMessage: message to ContactsViewController but haven't actually implemented it in ContactsViewController, you'll still get a compiler warning. In other words, #optional just means you don't have to implement it, but the compiler may still give a warning if you haven't implemented it but try to use it.
What you might want to do in Datastore is this:
if ([delegate respondsToSelector:#selector(didAddMessage:)]) {
[delegate didAddMessage:theMessage];
}
rather than just:
[delegate didAddMessage:theMessage];
(You'll still get a compiler warning in the first example, but it's safe to ignore since you're checking at runtime to see if the appropriate method is implemented in the delegate.)