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.
Related
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.
I often heard, that it is not good to organize classes the way every class knows every other class.
So I try to let the classes that are as a property in other classes not know about their parents. But with UIView I can't make it happen.
The question comes from a more generic background, because the problem occurs not only on UIViews.
If I have a car as object with 4 wheels as 4 instance variables and when the left front wheel bursts. The left rear wheel should get a message. How should I design the system in a good way?
An example with UIView in Objective-C:
I have an custom UIView filling the whole screen. managing all the layout. Let's say only 2 subviews keep it simple.
#interface BackgroundView : UIControllerView {
CustomViewA *buttonA;
CustomViewB *buttonB;
}
#end
#interface CustomViewA : UIButton
#interface CustomViewB : UIButton
Now if someone presses button A, button B should do something (e.g. turn red).
There are several options I see:
Set the delegate of button A to button B, so all event of A goes to implementation file of B. Problem if not all events have to do with button B.
Create a singleton of BackgroundView and let button A get the property of button B or call a method in BackgroundView that does forward my call to button B. Problem if I want more than one Background, could create a even higher class to have the two BackgroundViews (doesn't sound nice).
Call parent implemented for UIViews and then like 2. call a method or change it directly. Problem when not using UIView, copy the functionality to other 'parent' classes ?
Is there a better way of solving such a problem? Or can I optimize one method greatly?
#Odrakir mentions Model-View-Controller here, and they're right -- that should be your first stop. In case it helps, here's how you might apply that pattern to your example:
You have a model class with a property color. In a document-based application, the document typically is the model. In a non-document-based application, it's common to hang the model off of the application delegate instance. For simplicity's sake, let's say we're in the latter case. You might have the following classes:
// "Model"
#interface MyModel : NSObject
#property (nonatomic, readwrite, copy) UIColor* color;
#end
// AppDelegate
#interface MyAppDelegate : UIResponder <UIApplicationDelegate>
#property (nonatomic, readonly, retain) MyModel* model;
#end
// "View"
#interface CustomViewA : UIButton
#end
#interface CustomViewB : UIButton
#end
// "Controller"
#interface MyViewController : UIViewController
#property (nonatomic, readwrite, assign) IBOutlet UIView* buttonA;
#property (nonatomic, readwrite, assign) IBOutlet UIView* buttonB;
- (IBAction)doActionA: (id)sender;
- (IBAction)doActionB: (id)sender;
#end
The basic pattern is this: Actions from the view trigger model-modifying actions by the controller (the target/action pattern). To achieve this, you would hook them up by connecting the action of button A to -doActionA: on the controller ("File's Owner" in IB) and (likewise for B). Then the action methods on the controller, when triggered, should modify the model, and invalidate any views that would need to be redrawn as a result of the model change. You might do this by plugging the views into IBOutlets on the controller and having the controller call [self.buttonA setNeedsDisplay]. Then, when your view draws, it should read, from the model, any state that it needs to draw correctly.
It's worth mentioning that on MacOS, you can eliminate the manual invalidation step by using bindings to link the view to the model. In that case, changes to the model will automatically invalidate the view. iOS/UIKit doesn't have bindings, so you have to do this invalidation by hand.
Probably all that logic belongs in a ViewController who knows everything about the views he controlls but the views don't know anything about the view controller or about each other.
Views should communicate with the view controller through delegation (or target-action) and is the view controller the one that should make decisions and maybe, forward messages to other views.
By the way, take a look at Stanford courses in iTunes University. I think it's the first or second class where they talk about MVC (Model View Controller). It's going to help a lot: https://itunes.apple.com/us/course/coding-together-developing/id593208016
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>