I'm creating multiple different UI subclasses which can be created using JSContext, and I'd like them all to inherit a certain set of methods.
The classes vary from NSButton to NSTextView. It would be nice to have a single subclass act as NSView parent class, but I can't figure out if this is possible — or how to begin searching.
For example:
#interface CustomButtonClass : NSButton
I'm looking for a way to make NSButton inherit from a custom NSView subclass.
Is there a way to go around this, or do I need to create multiple intermediate classes?
The answer was super obvious, but coming from different languages, I struggled to make the connection. Thanks to all the comments, it was super easy to figure out.
Rather than trying to insert an intermediate class somewhere in the inheritance tree, you can create a category for NSView. Categories can also include all the required protocols.
For example, in my case, I wanted to expose multiple NSView methods to JSContext, and create some of my own, which would work on any NSView-derived object.
#protocol JSInterfaceExports <JSExport>
// Anything you want to expose to JS
- (void)customMethod;
// You can also expose all the common NSView properties
// and methods to JS in this protocol
#property (nonatomic) NSRect frame;
- (void)removeFromSuperview;
#end
#interface NSView (JSInterface)
// Any custom methods and properties
- (void)customMethod;
#end
Include this file on any NSView subclass, make them comply to <JSInterfaceExports>, and you are set.
Related
Consider an example when I'm having UIViewController with simple interface:
#interface MainViewController : UIViewController
#end
When it receives viewDidLoad or viewDidAppear messages it creates and places additional views.
When unit testing it, I want to mock up these views, so I need to change interface to this:
#interface MainViewController : UIViewController
#property (nonatomic) UIView *additionalView1
#property (nonatomic) UIView *additionalView2
#end
By doing this I'm moving responsibility of creating additional views to calling side, which, in my opinion, breaks encapsulation.
How can I avoid exposing so much of controller internals?
To support setter injection, you really have to expose it.
There are ways of trying to hide it, or mark it as off-limits. You can wrap the property declarations in #if DEBUG. Or you can move them to a class extension in MainViewController_Private.
But I find that these tricks only make the code noisier. So my approach is to go ahead and expose them. As I point out on Testability, Information Hiding, and the Class Trying to Get Out, such exposure can be a clue that a class may need to be extracted, or responsibilities shifted. For example, since you want to inject these views, should MainViewController really create them? Maybe the tension is leading us to make a factory, keeping MainViewController ignorant of the details of these views.
I've seen several Obj-C tutorials. The delegate classes all inherit from NSObject. For example, the applicationDidFinishLaunching delegate method, in some tutorials, it inherited from NSObject but NSApplication to implement it. The reason I don't think it should inherited from NSObject is that I didn't find any delegate protocol declaration in it, but I found that delegate protocol declaration in NSApplication. My Objective-C toy environment is GnuSep.
Here is some code:
#interface browserController : NSObject //here. inheriting from NSObject,but NSObject don'have any protocols declaration about applicationDidFinishLaunching.
{
NSBrowser *browser;
}
#end
#implementation browserController
- (void)menuAction:menuItem
{
..............................
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSWindow *win;
ActiveBrowserDelegate * abd;
WindowDelegate *wd;
NSRect wf = {{100, 100}, {600, 500}};
NSRect bf = {{10, 10}, {580, 350}};
.............................
}
It is called informal protocol (though GNUstep declared it anyway as GSAppDelegateProtocol for documentation purpose) NSApplication will simply check it at runtime if your delegate object will respond to the message, (using -respondsToSelector:) A delegate can be a view, a string, a proxy, anything as long as you make it responds to the selector. You don't need to make your delegate implement every method in such protocol since all verifications would be done at runtime. To make it looks cleaner you could just redeclare -applicationDidFinishLaunching: in #interface though you don't really need to, just make one in the #implementaiton is enough.
A delegate may inherit from anything appropriate. It is usually supposed to implement a certain protocol.
A protocol is a way of implementing a formal communication interface between two classes.
However, it is most unlikly that a delegate will inherit from its communication partner class.
With other words: Protocols are often used to overcome the unavailability of multiple inheritance. (Pretty much like interfaces in Java)
Example: A UIViewController subclass' instance controls a view that contains a UITableView. Rather than subclassing the UITableView for the implementation of its look or data, there are two delegates assigned to the table view object. One delegate serves as provider for custom layout (provides items such as the header view) and another (?) delegate provides the data that is being displayed.
Now, this delegate could be any object, inheriting from NSObject and implementing the two protocols. This object cold then be instanciated by the view controller and assigned to the table.
However, it is common practice that the view controller itself serves as delgate for the table(s) that it controls. That is a good pattern but strictly spoken not required. It could be any object.
Now the custom view contoller inherits from UITableViewController (which already implements the protocols and inherits from ViewController) and serves as delgate for the table view. The table view itself could be any subclass of UITableView. (Although this is a bad example here because subclassing UITableView is normally not advisable)
If the delegate does not need to inherit from any class and just implements the protocol, then it shold at least inherit from the cocoa base class NSObject. That ensures that it inherits all the usual capabilites and behaviour of any object. (init method, copy method, description method etc.) That may be required to work properly with other classes of the framework such as beeing used as an object within an NSArray, NSLog etc.
I have a two different ways of representing data in my app: via UITableView or UIScrollView.
So I have 2 main classes: AppTableView: UITableView and AppScrollView: UIScrollView.
And I want to implement the same additions to both views. So I wrote two classes: SomeAdditionsTableView: UITableView and SomeAdditionsScrollView: UIScrollView. The code of this classes is the same.
Main classes now looks like
AppTableView: SomeAdditionsTableView and AppScrollView: SomeAdditionsScrollView.
How to avoid this code duplicate? Thanks in advance.
Yeah this is a problem with the lack of multiple inheritance in Objective-c. I had the same problem when needing certain methods on a subclass of UIView and UIScrollView separately here: Subclassing UIView vs UIScrollView. There are 3 possible solutions I know of:
If you don't need to store any kind of instance variable, simply declare a category on UIScrollView and make sure to import that category into the two subclasses. This is the easiest solution, but least likely to work since you probably need to store state information if you're subclassing anyway.
Only create a subclass of UITableView and simply don't use it as a UITableView when you don't want a UITableView. You can technically just use a UITableView as a UIScrollView without invoking any of the tableView's methods. Of course, you're going to end up carrying around the 'weight' of a tableView (all of it's instance variables) but there no reason you have to use a UITableView as a UITableView and not just a UIScrollView.
Delegate as much of your code to a separate object to minimize code duplication. In each separate subclass carry an instance variable that is the method delegate and forward method calls to that delegate. Now here's where it gets fun. You can use protocols to declare the delegate methods in your subclass and override a special NSObject method: - (id) forwardingTargetForSelector:(SEL)aSelector to make sure those method calls get sent to the delegate. You use a category on the subclass that conforms to the protocol declared in the delegate class. This will expose all the methods of the delegate class in the subclass without requiring you to actually implement those methods in the subclass. When the runtime can't find the declared method in the subclass, it will call - (id) forwardingTargetForSelector:(SEL)aSelector, which you can use to return your delegate/forwarded class. This will prevent you from needing forward each individual method. Depending on what those method calls do, this may take a little more 'wiring', but it'll save you a lot of code writing in the end. It essentially 'mimics' multiple inheritance in objective-c using protocols. See my question/answer here for more details: https://stackoverflow.com/a/9419587/1147934.
Of the three, the last option tends to work the best for me. It takes a little work to get your head around but will significantly reduce code duplication. I also use it when I want to subclass without subclassing. The biggest requirement, though, is any class that you want to do this with will have to move it's method declarations out of it's interface into a separate protocol. But it's really not a big deal and the benefits of getting 'multiple inheritance like behavior' is great.
Also, there are times you may need the forwarded class to access instance variables in the forwarding class (the subclass). You can achieve this by using a delegate pattern whereby the forwarded class maintains a weak reference to the forwarding class in order to access those instance variables. For example, in your case, if you're trying to delegate methods that operate on a UIScrollView, those methods may need to be able to access that view. If those methods are stuck in a delegate class, they won't have direct access to the view's variables unless you give it to them. As usual with any delegate pattern, be very careful you don't create a retain cycle.
If your additions don't need any state of their own, you can make them a category on UIScrollView. Then, since a UITableView is a type of UIScrollView, you can use the category methods on one of those too.
If they do need to define new variables, then I would make it an independent class and have a MyTableView : UITableView subclass with a SomeAdditions property and, similarly, MyScrollView : UIScrollView.
You can achieve a lot by using protocols and "has_a"-relationships, instead of inheritance's "is_a"-relationships.
One very common pattern is delegate, but protocols are also useful for forwarding methods calls to encapsulated or wrapped objects.
in the following example to classes, that are not related to each other, share a common object, but it is also possible, that objects of the same kind use objects of different classes, that all implement a common protocol, so equal objects could do very different stuff.
#interface ClassA : NSObject
#property (strong) id<BrainProtocol> *brain
#end
##implementation ClassA
#synthezise brain;
-(void)theMethod
{
[brain theMethod];
}
#end
#interface ClassB : NSObject
#property (strong) id<BrainProtocol> *brain
#end
##implementation ClassB
#synthezise brain;
-(void)theMethod
{
[brain theMethod];
}
#end
ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];
//A object, that implements the BrainProtocol
Brain *brain = [[brain alloc] init];
[a setBrain:brain];
[b setBrain:brain];
When you have an UIViewController and UITableViewController classes and you wanted to let these two do some common stuff in their - (void)viewDidLoad how could you achieve this in Objective-C without actually duplicating your code?
I tried to create MyUIViewController inheriting UIViewController and implement viewDidLoad in there. This perfectly works with UIViewController classes obviously, but won't work in UITableViewController, since I can't simply replace #interface MyTableViewController : UITableViewController with #interface MyTableViewController : MyUIViewController.
I believe this topic is about "multiple inheritance" in Objective-C language, but other than figuring out what's different in Objective-C, I'd really like to know how to do guys do such thing?
This thread has some good information. One of your main options is to make a class with that shared functionality and hold it as an instance variable, then forward messages to it in forwardInvocation.
I have some functionality that I need in all my classes which derive from either UIView or UIImageView.
More specifically, I have gesture related code that both these classes need.
Currently my implementation is:
UIGestureView : UIView
UIGestureImageView : UIImageView
and make all classes derive from these.
These classes will again contain methods that the derived class will implement.
My problem is that the gesture handling code is duplicated in UIGestureView and in UIGestureImageView.
The natural thing here (as a c++ programmer) would be to use multiple inheritance
UIGestureView : UIView, GestureHandler
UIGestureImageView : UIImageView, GestureHandler
and let GestureHandler perform all the generic work but as far as I have understood this is not possible.
What is the objective-c way of doing this (without too many levels of child calling parent etc.)?
Just to stress, the problem is how to avoid implementing the same code twice, once in UIView (or its derived class) and again for UIImageView (or its derived class).
You could extend UIView with your gesture handling, and then every objects that inherits from UIView will have the methods you want. Not quite as awesome and subclassing, but would work on the global scope.
#interface UIView (GestureHandling)
- (void)didMoveAFinger:(UITouch*)touchOrWhatever;
// etc., etc.
#end
I have had similar issues with UIViewController and UITableViewController. I have a subclass of one that I want to share code with the subclass of the other. Yet there is no common place to inject that code if you want a subclass. The alternative is categories on the common superclass.
Check out categories: http://macdevelopertips.com/objective-c/objective-c-categories.html
You should try using protocols and delegation. They solve the multiple inheritance problem in objective-c.
#protocol MyProtocol <NSObject>
//Method declarations go here
#end
#protocol MyProtocol2 <NSObject>
//Method declarations go here
#end
#interface CustomView : UIView <MyProtocol, MyProtocol2>