In my TheTabBarController, without conforming to UINavigationControllerDelegate protocol, i could assign my class to moreNavigationController.delegate.
// without conforming to protocol, <UINavigationControllerDelegate>
#interface TheTabBarController : UITabBarController
self.moreNavigationController.delegate = self;
It just raises the following warning but compiles successfully.
Assigning to 'id' from incompatible
type 'TheTabBarController *const __strong'
The protocol's method is called at run-time without any error. I use it to hide more navigation bar for some view controllers.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
So, i want to know, is this legal and safe or; will it crash later or leak memory? How could this be allowed semantically? How could run-time resolves the method although its not defined in the interface and the protocol is not conformed? Or, UITabBarController uses a hidden category that conforms to the protocol?
Protocols have no runtime meaning. They are only used during compilation to display type errors when you try to do things like you are doing now. Why would you not want TheTabBarController to be a UINavigationControllerDelegate if it implements the protocol?
In Objective-C, you can call any method on any object and it might handle it by implementing forwardInvocation:(NSInvocation *)anInvocation or one of the related methods. You can also add new methods to an object or class at runtime using objc_install_instance_method and related functions.
Related
We are in the process of migrating existing classes from Objective-C to Swift. In a view controller class I am implementing a protocol and attempting to add the view controller as a delegate of an object contained within it. When I try to add 'self' as the object's delegate in Swift I get the following compile error:
Cannot call value of non-function type '((ListenerProtocol) -> Void)?
Here is the existing implementation in Obj-C
#interface SomeViewController : UIViewController <ListenerProtocol> ...
And adding the class as the listener (delegate)
[manager addListener:self];
This works without any problems. But the Swift version fails in what seems like the same code. Here is the Swift version of the same call:
#objc class SomeSwiftViewController: UIViewController, ListenerProtocol ...
And the call to 'addListener'
manager?.addListener(self)
I have verified successfully that 'self' is a ListenerProtocol object at runtime by checking:
if self.conformsToProtocol(ListenerProtocol){
// ...
}
In the object containing the delegate property the addListener method is defined in Objective-C as follows:
- (void)addListener:(id<ListenerProtocol>)listener {
// ...
}
The Swift class completely implements all of the methods defined in ListenerProtocol. I can't understand why this doesn't work in Swift. Can anyone make a suggestion? Thanks!
The problem is not with ListenerProtocol, it's with the protocol that manager implements. Judging by the type, addListener appears to be provided as an optional method. Notice the question mark at the end of the function type:
((ListenerProtocol) -> Void)?
This usually happens with (actually, I think that it only happens with) Objective-C optional protocol methods.
You should be able to write manager?.addListener?(self) or manager?.addListener!(self).
Environment: Xcode 6.1.1 & Xcode 6.2 Beta
Greetings:
I need to publish a NSString within a Swift doc from a neighboring Objective-C doc within the same project. For example, display "Hello World" generated in Objective-C upon a Swift page. I've made a proof-of-concept demo; based on feedback.
I'm thinking of using an ObjC --> Swift delegate via a protocol pattern as shown below:
Note: the Swift file is the delegate.
Here I'm calling the delegate method in Swift, from Objective-C:
#pragma mark - Action methods
- (IBAction)sendDelegateAction:(UIButton *)sender {
[_delegate radiusString:#"Hello World"];
}
I've instantiated the Objective-C file to link the delegate to the instance (I hope I got it right):
let geo32Controller = MyObjCTableViewController()
geo32Controller.delegate = self
So far, the compiler complained that the Swift protocol couldn't be found.
Here's the protocol (declared in Swift):
#objc protocol DiscoveryContributeProtocol {
// optional
func radiusString(radiusString:String)
}
And here's the delegate reference to that protocol in the Objective-C header file:
#interface MyObjCTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id<DiscoveryContributeProtocol> delegate;
#end
However, the compiler can't find the protocol:
BTW: when I put the bridge reference in the ObjC's header file, I get a compiler error:
Two Questions:
Do I have the correct pattern (did I instantiate the ObjC correctly) ?
How do I make the Objective-C portion see the Swift protocol for the delegate link?
You have the right idea, but have a few bugs that are preventing this from working.
You've declared Geo32Boundaries as conforming to the DiscoveryContributeProtocol, but it doesn't need to and doesn't actually implement it, it only has a property that conforms to that protocol. That's the source of the "Method 'radiusString:' not implemented" error:
#interface Geo32Boundaries: UIViewController // <-- that's all you need
You're setting the delegate incorrectly -- the code you have there looks like it's trying to set a class instance of Geo32Boundaries to self, but you're also trying to call it like a function. You'll need to set the delegate on a the instance of the Geo32Boundaries view controller that is being presented to the user. I don't know where that code lives, so I can't give a great example, but it'll be something like:
geo32Controller.delegate = self
Lastly, though not a bug, your protocol should really be called DiscoveryContributeDelegate -- we usually don't use "protocol" in the protocol name.
Another way of phrasing this question: is it possible for a subclass to be a delegate of its super class? I'm trying to make my code reusable within my app and have a situation where the subsclass needs to implement two methods for it to be functional. How can I ensure this occurs? Or what is the proper way of defining these methods?
Update
I didn't mean to imply that I want the compiler to generate flags. I just want a clean way of organizing my code. Currently I override methods of the superclass. Using that approach the superclass can call [super methodToOverride] and it works. However this doesn't feel very clean to me as there's no way to specify "these are the methods you should override" aside from putting a comment somewhere.
In obj-c, it is not possible to force subclasses to overwrite methods of its superclass. But you can raise an exception in the superclass, should it ever be called because the subclass did not implement a certain method.
But a subclass can be a delegate of its superclass, if the superclass does not implement certain methods, and you can enforce that the delegate implements these methods, if the superclass specifies the protocol, i.e. required methods, and the subclass adopts it.
If you want to force your subclass to implement methods from super class, you can do this as below:
//In super class
- (id)someMethod:(SomeObject*)bla
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
Your app will crash if subclass will not implement this method and you don't need to call
[super someMethod:bla];
There is no way to do this in compile time. However you can raise an exception in the base class.
Something like this:
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:#"You must override %# in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
If your question is "how can I get the compiler to flag that a certain class doesn't implement a certain function" then I would say
Define a protocol with non-optional methods -- "By default, all methods declared in a protocol are required methods. This means that any class that conforms to the protocol must implement those methods."
Define a class ("stub") that declares it implements the protocol
Now when a subclass of your stub class is written, the compiler will flag it as an error if the mandatory method(s) aren't implemented
I know that it's awful, but supposed that you need to do this since your 3rdParty SDK requires this design pattern, you could use a Factory pattern:
Supposed then to have the base class MyParentAPIClient and two sub classes like MyFacebookAPIClient and MyGooglePlusAPIClient and that you do something like
self.myAPIClient = [MyParentAPIClient alloc] initWithAPIKey:apiKey];
and that you have defined
##interface MyParentAPIClient : NSObject {
}
-(void)callAPI;
#end
and you have override this in the two subclasses
#implementation MyFacebookAPIClient
-(void)callAPI {
[super callAPI];
// do something specific for this api client
}
#end
and
#implementation MyGooglePlusAPIClient
-(void)callAPI {
[super callAPI];
// do something specific for this api client
}
#end
Then you are doing in your controller
[self.myAPIClient callAPI];
but the super class MyParentAPIClient method is being called.
Now you could do a factory in the base class like:
-(void)callAPI {
if([self isKindOfClass:[MyFacebookAPIClient class]]) {
[((MyFacebookAPIClient*)self) callAPI];
} else if([self isKindOfClass:[MyGooglePlusAPIClient class]]) {
[((MyGooglePlusAPIClient*)self) callAPI];
}
}
Of course this have a downside that is to do not call the super in the sub classes that now become:
#implementation MyFacebookAPIClient
-(void)callAPI {
// [super callAPI]; the factory method called that
// do something specific for this api client
}
#end
and
#implementation MyGooglePlusAPIClient
-(void)callAPI {
// [super callAPI]; being called in the factory
// do something specific for this api client
}
#end
The good news is that there is no change in the methods calls since as soon as you call from the controller:
[self.myAPIClient callAPI];
You will have the calls
[MyParentAPIClient callAPI]; // parent class
[MyFacebookAPIClient callAPI]; // sub class
The other downside is that the parent class must known the subclass instances.
Now if we take a look at the factory:
if([self isKindOfClass:[MyFacebookAPIClient class]]) {
[((MyFacebookAPIClient*)self) callAPI];
} else if([self isKindOfClass:[MyGooglePlusAPIClient class]]) {
[((MyGooglePlusAPIClient*)self) callAPI];
}
}
we could make it better like in several way. Take a look at Dynamic type cast from id to class in objective c and Is there an equivalent to C++'s dynamic cast in Objective-C? or Objective-C dynamic_cast?
Good luck!
The UIGestureRecognizerSubclass.h pattern from UIKit is worth a look, that has all the protected methods that should be overridden and that header is not in the framework include, it is only included in subclasss' .m files. Also, nowadays you can tag methods with NS_REQUIRES_SUPER to require overrides to call super, however it can only be used in interfaces, not protocols so that might influence your design.
For super advanced code, NSAccessibilityProtocols.h in AppKit uses a protocol tag to require subclasses to re-implement methods, even if already implemented by a superclass. Here is an example of that you can paste right into in header in your currently open Xcode project:
NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION
#protocol Protocol
#property (readonly) id theWorstOfTimes;
// -(void)testMethod; // uncomment to test problem
#end
// In this example, ClassA adopts the protocol.
#interface ClassA : NSObject <Protocol>
#property (readonly) id theWorstOfTimes;
#end
#implementation ClassA
- (id)theWorstOfTimes{
return nil; // default implementation does nothing
}
-(void)testMethod{}
#end
// This class subclasses ClassA (which also adopts 'Protocol').
#interface ClassB : ClassA <Protocol>
#end
#implementation ClassB // expected-warning {{property 'theWorstOfTimes' requires method 'theWorstOfTimes' to be defined - use #synthesize, #dynamic or provide a method implementation in this class implementation}}
#end
In Xcode you'll see a yellow line at ClassB's expected-warning that the property method is missing. NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION is just a macro for __attribute__((objc_protocol_requires_explicit_implementation)) and this code sample is modified from the test harness of that feature here.
Although this looks great there is a slight problem. Currently this only works for methods that implement protocols, it used to work also for methods but a bug has been introduced in 2014 via a misunderstanding on the purpose of this feature and thus now it is limited to property methods. I have emailed the author to make them aware so hopefully it changed back to its original and proper behavior. To test the bug you can uncomment the method in the protocol and you will see there is no warning in ClassB. Hopefully you can change some of your methods to read-only properties to at least get some use out of it. On the plus side when Xcode offers to "Fix" the issue it does add stubs for the missing methods.
Here is some documentation on NS_PROTOCOL_REQUIRES_EXPLICIT_IMPLEMENTATION:
ImplementingAccessibilityforCustomControls
nsaccessibilitybutton
If you used this then pat yourself on the back for becoming an ObjC expert if you weren't already!
I have developed a generic controller for sliding UIView and I manage UIViewController (and obviously subclass ) and must call a specific method (freeze) only if a subclass of UIViewController respond to this method:
-(void)freezeRootViewController
{
if([_rootViewController respondsToSelector:#selector(freeze)])
[ ((id) _rootViewController) freeze];
}
I don't know the class of _rootViewController but I know it is a subclass of UIViewController, for this reason I have tried to cast my _rootViewController to ID, but I can't compile:
Environment: XCode 4.5.1, iOs 4.3+ with ARC
Error: "No know instance method for selector 'freeze'"
Note: I CAN'T force the developer to use a specific UIViewController subclass for the _rootViewController.
After checking if a "generic" type responds to a selector you invoke it by using performSelector:/performSelector:withObject: if it is a simple 0-1 parameter method, otherwise use an NSInvocation. Since freeze has no arguments you would just use performSelector:.
-(void)freezeRootViewController
{
if([_rootViewController respondsToSelector:#selector(freeze)])
[_rootViewController performSelector:#selector(freeze)];
}
This seems to be a warning rather than an error - you should be able to compile your code and just ignore this diagnostic message. However, if you want to get rid of it completely, you can declare a protocol and do the cast as follows:
#protocol MyProtocol <NSObject>
- (void)freeze;
#end
[(id <MyProtocol>)_rootViewController freeze];
In my view controller's viewDidLoad method, I create an NSURLConnection
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest
delegate:self]
You can see I set the delegate to self.
Then I implemented the delegate method
-(void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) reponse {
//myImplementation;
}
This implementation is only defined in the #implementation ViewController #end block, and it is not declared in the ViewController's #interface.
So I guess this method is private? It compiles and runs well. But I just can't call this method like [self connection: connection didReceiveResponse: response] in the ViewController's own methods.
What's the explanation?
The methods are declared via your inclusion of the NSURLConnectionDelegate protocol in the class's interface definition:
#interface MyViewController : UIViewController <NSURLConnectionDelegate>
This tells the compiler that your class promises to implement all the required methods of that protocol, and that it may implement the optional methods (as it happens, this particular protocol has only optional methods). So the declarations exist publicly, they're just in another file.
The method is "private" inasmuch as it is possible to have a private method in Objective-C. You actually can call the method from outside of the class, even if it's not declared in the interface. This is possible by way of Objective-C's dynamism in how it handles method calls -- any object can receive any message (the obj-c lingo for method), but whether or not the object can actually do something with that method depends on any number of things. Check out Object Messaging in Apple's docs.
The short answer is that your code works as you have it because an implementation of your method exists in your class, and Objective-C knows how to find it at runtime regardless of your class interface.