I tend to agree with Joe Conway’s and Aaron Hillegass’s analysis, as reported today by Ole Begemann in http://oleb.net/blog/2012/01/initWithNibName-bundle-breaks-encapsulation/
Basically, they state that the NIB's filename is an implementation detail of the corresponding UIViewController class, and that it is not the business of the calling class to pass in the NIB's filename in the init method.
I was wondering if there was any particular reason for this design choice from the creators of AppKit/UIKit, or if it is merely a mistake — and, in the latter case, why it wasn't corrected when UIKit came out, which would have been a good opportunity.
If any Objective-C old-timer could provide the historical background to this, that would be nice to get a better understanding of the framework we use every day.
I suspect it was done this way so that a UIViewController could have basic functionality as a controller without requiring any subclassing. For example, if you're just pushing a "Credits" view on a navigation controller, and the view has nothing but static text, you could get away with not creating a UIViewController subclass. You could simply create a UIViewController directly and pass it the nib that contains your static text.
Most of the time, of course, you're going to want some level of interaction with the presented content, in which case a custom controller is necessary. But in theory, it's not always required.
It has come to my mind today that maybe Storyboards are precisely a response to that issue. Because Storyboards are defined at application-level, there is no more violation of encapsulation, merely a change of level. UIViewController subclasses become the detail implementation of the Storyboard as a whole.
It still doesn't explain the historical reasons behind the original design, but at least they have done something to address the issue — and, as often with Apple, in a very elegant way.
Related
I'm a bit of a newbie programmer, so I'm asking for people's opinions/advice on what they would do in the situation I'm in.
Background
Due to constraints of the library I'm using to achieve a "pop up" like window, I'm currently working on a UIView class (let's call it Menu) which creates UIImagePickers and various other View Controllers (VCs). However, since the UIView itself persists on top of any VCs it spawns itself, I'm forced to delegate any VC creation methods to the parentVC of Menu, so that I can dismiss the Menu view, create any VCs necessary, and then (sometimes) additionally restore the Menu view.
Issue
I'm currently copy and pasting hundreds of lines of delegate methods for any UIViewController that has a Menu view. These delegate methods are the same for every one of those view controllers, and exist there because the methods have UIViewController specific functions such as presentViewController. I'm tempted to pass the parentVC as a parameter to Menu in order to cut down on the amount of duplicate code, but this seems MVC-unkosher. What would be a good approach to this problem?
If I understood right, you can Just create an extension, like this
extension SomeClass: ClassesDelegates {
//some specific code that all Views will use
}
I'm tempted to pass the parentVC as a parameter to Menu in order to cut down on the amount of duplicate code, but this seems MVC-unkosher.
That's because MVC is a dated architectural choice, leading to exactly this kind of situation. That's where the "Massive View Controller" snark that you may have noticed here and there around the web comes from.
Your Menu depends on parentVC. Thus, we call parentVC a dependency. A fundamental precept of modern architectures is dependency injection, which allows you to eliminate the duplicate code you refer to and test Menu in isolation from its dependencies. Check out
Dependency Injection Demystified
Dependency Injection
Dependency Injection, iOS and You
Dependency Injection in Swift
So, fear not the use of parameters, your instincts are sound. Embrace the use of parameters, and preen that you are adopting the "25-dollar term for a 5-cent concept" (see first link), the Agile design pattern referred to as Dependency Injection. For bonus cool points, do not make that parameter a UIViewController. Define a protocol with just the access that Menu requires, and make that parameter anything that conforms to said protocol, and add an extension to UIViewController conforming to that protocol. That makes testing and reuse elsewhere easier; and more importantly, then you will be engaging in Protocol-Oriented Programming, which puts you right out on the cutting edge of trendy Swift design patterns. Nobody will call you a newbie then.
In methodA of my view controller VC1 another view controller's (VC2) method methodB is called in which VC1 is deallocated. Then control returns to VC1 methodA which obviously crashes when self is used.
It is not obvious that the instance is deallocated, so developers may use self without knowing that they should not. From that perspective I'd like to fix the deallocation. However, I'd like to get some opinions whether or not such a situation is common or needs to be fixed ASAP a.s.o.
Q1: Is execution of deallocated object's methods somewhat common which one will encounter in typical medium projects?
Q2: Would it be acceptable, if comments are added, which warn the developer?
Q3: Are there any other recommendations / opinions?
The project is of medium complexity (about 200 classes of which 50 are view controllers). I'd like to get a feeling about how much effort I should invest to take care of such "deallocated method calls". If it would be one of my smaller pet projects, it would be rather easy to fix anything. However, with an inherited project which already went through a couple of hands, things are not so trivial any more.
EDIT:
didFinish delegate scenarios:
Thinking about it, I came across didFinish kind of delegate calls. Assume a master view controller (MasterVC) uses a slave view controller (SlaveVC) to do some work, keeps a strong reference to it and sets itself as a delegate for the SlaveVC. When the slave is finished, it calls slaveVcDidFinish. In MasterVC's implementation of slaveVcDidFinish the strong reference to the SlaveVC is set to nil. So when the slaveVcDidFinish returns, it is SlaveVC's responsibility to never use self, as it may have already been deallocated.
- (void) notifyDelegate
{
[self.delegate slaveVcDidFinish];
// From here on, `self` may be invalid...
}
This likely is relevant, when the SlaveVC is dismissed before the call to slaveVcDidFinish, as otherwise it cannot be deallocated because it is part of the view controller hierarchy.
Q4: Is my understanding of this didFinish scenario correct?
[/EDIT]
A few details, in case it is relevant:
VC2 presents VC1 and keeps a strong reference
VC1 does its work and needs to present VC3 for which it dismisses itself before
VC3 does its work and delegates back to VC1's methodA
VC1's methodA delegates to VC2's methodB
methodB releases the strong reference to VC1, VC1 now gets deallocated (dealloc is called), and control returns to methodA
methodA does a little more work and then returns
One way to do the little more work was to call methods on an object passed into methodA as parameter. That should work, as self does not play a role there.
Another way to do that work was to call a method of VC1 using self which obviously causes a crash. So as long as one does not use self, everything should be fine.
Yes, you should fix it, but based on what you are saying your app architecture must be to a greater or lesser extent wrong. You are trying to implement an algorithm distributed across view controllers that really needs an object that lives across the lifetime of the objects you are dealing with. Such needs a mediator object.
Actually I suspect you may not need to be implementing that algorithm at all and another approach that works better with how controllers are instantiated and how control gets passed around the app would work fine, but you haven't posted sufficient info for me to be able to advise on that.
However having said that, implementing an overarching controller could certainly be used to solve your problem. A good way to do this is often to subclass your current root view controller (or better still a subclass that implements a category you define). So for example, if it is a UINavigationController you are using, create UINavigationController subclass, change the class of it's representation in your Storyboard (assuming you are using storyboards) and do the work of coordinating display and dismissal of other view controllers in there.
Also check out the Mediator design pattern http://cocoapatterns.com/ios-view-controller-transitions-mediator-pattern/
As a note, generally these days you can avoid having any object properties for strongly holding view controllers. Usually the root view controller holds strong references to any view controller it needs to present, lazy invocation should be used to present view controllers that are not currently presented or stored in an existing navigation controller stack (which keeps the memory utilisation profile in good shape) and any other child view controllers can be added in to your view controller heirarchy using the addChildViewController: method in which case they are strongly held in the childViewControllers array property.
You may want a convenient name for a view controller, but actually I would recommend writing a small bit of code for as the implementation of any such property that will identify the controller you need from amongst those in the Apple supplied properties, and dynamically retrieve it. This may seem like hard work, but it's worth it and actually, paradoxically, decreases code complexity and helps ensure you stay working with the view controllers the way Apple intended. Doing anything else increases pain.
Using separate object properties for holding references to view controllers will usually only duplicate mechanisms the existing Apple classes already provide you with. Such unnecessary duplication increases code complexity and can introduce bugs and memory management issues (of the kind you are in fact describing).
Sorry this answer is quite general, but you are seeking an answer to the wrong question. From what you have said it's clear that at some level you need to address your app architecture.
I agree the architecture seems flawed but as you say it is inherited code and not trivially small maybe some compromises must be made.
I am thinking since self (or any instance variables or properties) cannot be called anyway perhaps you could make the methods you need to call class methods (+) rather than instance methods (-). Class methods are obviously safe to call without an object instance.
OK, this may sound very basic (especially for someone who has written tens of thousands of Objective-C code), but I've always tried to avoid all this... or just tweak existing solutions. The result? I've never learnt how to do something simple like that.
So, here's my ultra-simple scenario:
I want to create a custom NSView (let's say a simple view with an image and a text in it), which I'll be able to assign in the Interface Builder (take an NSView element and set its class to MYCustomView - that's all - nothing more complicated)
I know I can write an NSView subclass and have it draw all my elements programmatically in drawRect: and all this - but I most definitely don't find any point in that.
What I do want is to simply draw the view in the Interface Builder (in our example, with a "placeholder" image and textfield), be able to use it as the "basis" of our NSView subclass, and also maintain pointers to the two elements in the view so that I can programmatically access them.
I know it's doable - I'm not asking about that. What I need is an ultra-simple walkthrough. Is there anything you can point me to?
Rephrasing the question in a... one-liner:
How can I replace the programmatic approach (seen in like 99.9% of NSView subclasses) in drawRect:, with a layout taken from a XIB?
P.S.
(A) Trust me, it must have been the 100th time I've been reading about NSViewControllers and all these, but not having used them, probably means that I still haven't found the point in using them...
(B) Please, don't shoot me with "what have you tried" questions. In the course of time, I've tried loads of things and at times I've somehow made it. However, it always feels like a crappy, messed up thing I just managed to get working. Nothing more, nothing less. All I want is to know if there is a simple tutorial on the above simple scenario.
(C) If I get an actual explanatory answer to this one, I guarantee I'll re-post it myself. You simply can't believe how many seasoned Cocoa developers have serious trouble dealing with this...
I've always wanted "custom" Storyboard classes as well!
This may not totally answer your question but this is just how we do it now, in iOS: just use container views.
Full extremely long tutorial: https://stackoverflow.com/a/23403979/294884
Everything's a container view in iOS now.
What we do is just have a scene, and then duplicate it: then change the colors or whatever as you describe.
Here's a literal example from the storyboard that was open behind this browser window!
Notice the small class/scene thing, we just copy it. Notice in the example it is slightly customised, just as you say. They are all the same class (it happens to be caled "BookBist") {"bist" == "bouncy list" btw}
Then as I say container views are the secret because, well, it's for exactly this purpose, it's why apple finally introduced "container views".
(BTW on that long container view tutorial. Search down to What if you want (say) a table controller or a page view controller instead of a UIViewController? it's a critical trick when making container views! Ridiculously Apple gives you a "default" VC when you drag in a container view; of course you never want that; in the example at hand I put the small BookBist scenes connected to the container views wherever they are needed.) Example...
Now, I 10000% understand what you are asking and have always wanted to know the answer myself!
For use HALF the answer, is, as I say, "copy the scene" so that works perfectly in modern storyboard. I appreciate that sucks, because what you want is a prefab, like in any game engine such as Unity3D, right? Me too!
But do note that THE OTHER HALF of your answer is certainly "container view magic" - "everything's" a container view now in iOS, indeed Apple finally put them in to make a rational way to do exactly the sort of thing you describe.
I have a UIView I need to access the properties of from all around my app. I know you can't create a Singleton around a UIView object, so what might be a good way of doing similar?
E.g. The view has a label. From any view controller in my app I want to be able to change the text of this view (sitting in a parent view controller).
Thanks.
EDIT:
Success! Using KVO to track changes in my Singleton object worked a charm, and a very simple solution.
I think what you’re trying to do violates the separation of concerns of the MVC pattern: The only thing that should interact with a view is its controller. In your case, you should probably be creating a model that is watched by your view controller (maybe through key–value observing), and then the controller can propagate the necessary changes to your view.
If you know (read: you really know for now and forever!) that there will be at most one instance of that view alive at one point in time, you can just use a global variable to store that. Or use a class property on that view - which is as close as being a singleton as possible.
Or you might just fix your design, which has proven to be the better choice in every case I can remember. :) Just add some forward and backward references in your classes (and stick to MVC principle). It takes much less time to implement that worrying about those awkward workaround, and it will pay of rather sooner than later.
I'm learning iOS development stuff and what I have found in tutorials and books is that controller layer usually has access to the View's controls directly (textfields, labels etc.). Let's consider such an example:
Assume, that View has a label called lblResult and a textfield called txtDataToAnalyze. Than in controler interface we've got something like this:
#property (nonatomic, retain) IBOutlet UILabel* lblResult;
#property (nonatomic, retain) IBOutlet UITextField* txtDataToAnalyze;
and some #synthesize statements in the implementation file.
I have some experience with JavaSwing development, where most of thinks I'm writing manually without any GUI Builders, and what I usually do in MVC is to access the View's controls via getters/setter. For example: void setResult(String resString); or String getDataToAnalyze();. In that way, controller knows only what pieces of information are displayed in the view, and not how are they displayed. I think it is more flexible (it is easier to change the view layer later).
I know that iOS has some specific rules, has introduced XIB/NIB files etc so maybe my doubts are completely useless in case of iPhone/iPad development. But I am going to write some more serious application for iOS (actually "rewrite" it from Java Swing) and that's why I would like to ask you:
Do you think, I should change the way I am thinking and get accustomed to that new (for me) approach (xib files, creating GUI using drag&drop and providing controler with information about how data should be displayed in view) ?? Did you have similar doubts when starting with iOS?
Short answer:
Yes, I think you should definitely spend a little time getting accustomed to working with Interface Builder (IB) to make NIBs and storyboards and let IB create the IBOutlet and IBAction references for you for those controls with which you need to interact. Once you're proficient at it, you'll be impressed by your productivity in generating easily maintained code. Don't dismiss IB too quickly.
In terms of letting the controller interact directly with the IBOutlet and IBAction references, this is common practice for simple user interfaces. If you have some real world examples, post a new question with a screen snapshot and we can offer more practical guidance.
Long answer:
Part of your question seems to be driven by the apprehension in seeing view controllers that are doing detailed interaction with a view's controls. The thing is, if you want to isolate your controller from some of the implementation details of the view, then go ahead and subclass the view and put the view specific stuff in there. IB can interface with both view controller subclasses as well as view subclasses. So you can happily use IB and still isolate your view controller from some of these implementation details.
Personally, I only do this subclassing of UIView when the view hits some subjective complexity threshold (e.g. for me, that threshold is when I find myself doing some complicated animation, such as using CADisplayLink; complicated gesture recognizers, etc.). I also subclass those subviews that are logical entities of their own (e.g. UITableViewCell or UICollectionViewCell). But for simple views where I'm interacting with my model to setting a control's properties, interacting with text fields, etc., I think putting that in the view controller is fine. Having said that, if I have a lot of view-specific code in my controller which has nothing to do with the integration of my model with my view, then start subclassing the UIView and shifting the view-only code into that.
Implicit in your question is the notion of programmatically building view rather than using NIBs/storyboards. In my opinion, using Interface Builder (IB) to build your UI is much easier to develop and maintain. There might be some pedagogical value to doing a test project where you build your views programmatically, so you really understand what's going on, but after that, I think you'll find yourself quickly gravitating to storyboards. And you'll get plenty of chances to write your own non-IB code when you start doing things beyond the capabilities of the standard IB controls (e.g. complicated custom container views, etc.). There are definitely those who prefer to develop views programmatically, but I don't think you can beat the development speed and ease of maintenance of IB generated UIs.
I general, the controller does not know about the view, but the view knows about the controller.
The gang of four book says:
"MVC also lets you change the way a view responds to user input without changing its visual presentation. You might want to change the way it responds to the keyboard, for example, or have it use a pop-up menu instead of command keys. MVC encapsulates the response mechanism in a Controller object. There is a class hierarchy of controllers, making it easy to create a new controller as a variation on an existing one.
A view uses an instance of a Controller subclass to implement a particular response strategy; to implement a different strategy, simply replace the instance with a different kind of controller. It's even possible to change a view's controller at run-time to let the view change the way it responds to user input. For example, a view can be disabled so that it doesn't accept input simply by giving it a controller that ignores input events.
The View-Controller relationship is an example of the Strategy (315) design pattern. A Strategy is an object that represents an algorithm. It's useful when you want to replace the algorithm either statically or dynamically, when you have a lot of variants of the algorithm, or when the algorithm has complex data structures that you want to encapsulate."