I have a large number of UIViews that are created in the app delegate applicationDidFinishLaunching and added to an NSMutableArray. The user pages through these views using a page control and scroll view. I created the UIView and its child components in IB. They are wired to a controller. To create 170 views on the iPhone takes about 30 seconds. Eventually, I'll have over 1000 views. Besides being slow, it will also kill the app because of memory use. How much faster and memory efficient is creating everything programmatically without views? How might some of the 6000 fact type apps be doing it?
Is there a better approach instead of creating everything at once? The user may access a view in slot # 400 and begin scrolling from there. Any suggestions are appreciated.
UIViewControllers are lazy. They only load when requested, and automatically unload when memory is tight (and it's easy to unload them manually by calling self.view=nil).
"Load" here means "read a NIB," or "build programmatically." ViewControllers don't really care. Building programmatically can be a little faster since you don't have to hit the disk, but this is seldom the bottleneck, since you only display one view controller at a time.
As to which way to go, it's more style than performance (with the exception of UITableViewCells, which there are reasons you need to build programatically in most cases).
Start by studying the View Controller Programming Guide. It'll show you how iPhone intends you to do this.
To eJames' comment about NIBs being XML files, this may be slightly misleading. NIBs are binary files generated by compiling XIB files which are XML. I'd do actually profiling on the phone before assuming that NIB load time is actually a problem. Much as I am by nature inclined to programatic layout, I've found in practice that NIBs greatly simplify many UI issues in practice, and I always come back to them for large projects.
It is difficult to suggest an answer without knowing a little bit more about your specific problem, but I would venture to say that if you expect to display 1000 different things, creating 1000 individual views in IB is not the way to go.
If your pages share a common layout, you can use a UITableView to display the content of each page, and store only the data for each page in your NSMutableArray.
An excellent tutorial on how to use UITableView can be found here.
If they do not share a common layout, laying things out programmatically will be the way to go. This should be no more memory or processor intensive than doing it using IB, and in fact it will probably be faster, since it removes the need to read and parse an XML file (which is what .NIB files actually are).
Related
I am aware that Apple is deprecating the use of NSCell in favour of NSView (see AppKit 10.10 release notes). It was previously recommended that NSCell be used for performance reasons when many controls were needed.
I have spent considerable time implementing a custom control that required many subViews and the performance using NSView-type subViews was not good. See related stackoverflow discussion What are the practical limits in terms of number of NSView-type instances you can have in a window? I was struggling with 1000-2000 in-memory objects (which doesn't seem a lot). What is the actual reason for this limitation?
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
If there are practical limits, then what are Apple thinking when they say they are deprecating usage of NSCell's? I am sure they are aware that some controls need a large number of subViews.
Further, an (probably outdated) Apple Developer Guide give the following explanation for the difference between NSView & NSCell which I need explained further:
"Because cells are lighter-weight than controls, in terms of inherited data and behavior, it is more efficient to use a multi-cell control rather than multiple controls."
Inherited data: this would surely only cause "bloat" if the data was being used => and it would only be used it you needed it?
Inherited behavior: methods that you don't use in a class/object surely can't cause any overhead ?
What is the real difference between the lightweight NSCell versus the heavyweight NSView other than that it just seems to be conventionally accepted?
(I would really like to know.)
A brief and incomplete answer:
NSCells are about drawing state, and not much else. NSViews must draw, but also must maintain and update all kinds of other information, such as their layout, responding to user input events, etc.
Consider the amount of calculation that must happen if you resize a view that contains many hundreds of subviews: every single subview position and size must be maintained according to the existing constraints for the view layout. That alone is quickly a very large amount of processing.
NSCells, in contrast, don't exist in a layout in that way. Their only job is to draw information in a given rectangle whenever asked to do so.
One thing that confuses me on the above is View-based Cocoa NSTableViews. You can create tableViews with more than 1000-2000 cells and they don't seem to have poor loading and scrolling performance? If each of the cells is an NSView then how is this achieved?
NSTableViews re-use views. The only views actually generated are those that are visible, plus maybe a row of views above and a row below the visible area. When scrolling the table, the object value associated with a given row of views is changed to that the views in the row will display different content
I want to add a button that when pressed will lock two sliders together such that the values for the two sliders will always be the same.
I have a solution for this using code, but I'm wondering if there is a way to do this with interface builder alone.
I am worried that the code based solution that one slider may lag behind the other in high CPU utilization environments.
No, there is no way to do this with Interface Builder alone.
Actually everything becomes code in the end, as far as I understand, Interface Builder was built to improve the development time, not necessarily to improve performance, I found this interesting quote on Apple's site about NIBs:
Xcode works in conjunction with these frameworks to help you connect
the controls of your user interface to the objects in your project
that respond to those controls.
Taking into account that, everything will become code (of some level). About NIB files.
At runtime, these descriptions are used to recreate the objects and
their configuration inside your application. When you load a nib file
at runtime, you get an exact replica of the objects that were in your
Xcode document. The nib-loading code instantiates the objects,
configures them, and reestablishes any inter-object connections that
you created in your nib file.
If you would really want to avoid such behavior probably the best you would be able to do is create the widget from scratch, but that would be a totally different question.
Just curious, why wouldn't you want to use code?
Locking the two sliders together in IB is easy. And I've never seen lag. Having that lock dependent on the press of a button is another story, that would have to be done in code, but it would not be too complicated. Assuming you have outlets connected in IB and declared in the controller
-(IBAction)lockSliders:(id)sender {
[slider1 setContinuous:YES];
[slider1 takeIntegerValueFrom:slider2]; // or takeFloatValueFrom or takeDoubleValueFrom
[slider2 setContinuous:YES];
[slider2 takeIntegerValueFrom:slider1];
}
I've been working on my first XCode/iOS project and so far it's been an enjoyable experience. Currently, I am working on a Nib file that acts as a primary display for other views. Within this Nib file, I have a primary UIView and next to it I am designing a series of seven other views. At runtime I swap these seven views in and out when necessary, within a container UIVie, located on my primary UIView.
Across my 8 total views that I'm designing in IB, I have a large amount of buttons, images and other objects that need to be linked to IBOutlets. As this project is getting bigger, it would helpful if I could split my class across multiple code files.
Using categories is the solution, however, I've hit an annoyance-- when I tried to create an IBOutlet in one of my category headers, the XCode compiler keeps giving me errors and warnings about ivars and outlets. In my Nib I have a large number of objects that need outlets and I'd prefer to not have to define all of them in one, huge code file if that's possible.
However, I understand that I have to work within my limitations, so my question is a bit different than asking how to add an IBOutlet to a category. Instead, I'd like to know what are the best practices for splitting up a large UIViewController class across multiple code files? What are the limitations of objective-c and what is the best way to structure your code when working with interface design?
My code is not quite yet unwieldy, but I'd really like to start restructuring it so that it will be much more manageable when it consists of 100's of methods and 1,000's of lines of code.
Any pointers would be helpful. Thank you!
You can create one NIB that contains several controller objects, each managing the relevant views, and that will help to keep your code more manageable. The downside is that your memory footprint is larger loading a large NIB.
To achieve this, define the controllers in separate classes with relevant IBOutlets, and then drag the NSObject from the objects tray (where the UILabel, UIButton etc... are) into IB. Then for each object, define its class to the the correct controller, and you will have access to all the correct outlets and actions. You can then hook the controllers into a single UIViewController to control how they interact with each other.
The preferred way though, would be separate NIbs for separate controllers, and to have them all as UIViewController subclasses. If you move to iOS5 they have superseded NIBs in IB with Storyboards. These allow you to have separate UIViewControllers in a single IB file, however they are more efficient in memory usage as they don't keep the whole object tree in memory.
This is a pretty loaded question. But, in my experience, I've found that IB causes more headaches for me than it's worth in complicated situations such as this. I recommend just forgetting about IB for this scenario and just create your views in code. IB is really supposed to alleviate some of the more mundane tasks of layout, style, etc.
I recommend that all of your views be placed in their own project files as this has paid off for me...nothing more painful than scrolling through 1000's of lines of code to find what you need...and worse yet, using that dreadful code collapse widget in the gutter.
This question has been on my mind ever since I started with iOS development: the UINavigationController and the usage of memory.
I see lots of apps like iMail, Find Friends, Notes, etc. where the UINavigationController makes perfect sense. They allow you to drill down two or three levels of hierarchy and that's it.
But imagine browsing a deep hierarchy, like your Mac's file system, starting from the root folder.
If I used a UINavigationController, I would keep on pushing hundreds of controllers on the stack (worst case). I don't consider that a good usage. There can be cases where the pushed controllers can become very heavy (as far as memory is concerned) and they just sit there for nothing.
I would wish there was a "dynamic" version of the UINavigationController: it would just tell you what to create when navigating back up the hierarchy instead of just popping up the hierarchy.
My question is now: is UINavigationController meant to be used for deep hierarchies? What are good alternatives if you want all the animation, bar items, etc?
Or do I see issues where there aren't any?
UINavigationController is dynamic. When you get low on memory, you can release the memory used by parts of the hierarchy that are not visible -- this is a manual step when you get a low memory notifiction -- and the OS automatically releases the views in the same situation.
When the top view controller is popped off the stack, your viewDidLoad method would be called, allowing you to recreate your view.
If it really is a problem, iOS5 allows you to create your own "container" views, so you could create your own navigation controller that does exactly as you suggest. Check out the "Implementing a Container View Controller" section of the UIViewController documentation.
Having said that, you might need to add some shortcuts to work your way around a very deep hierarchy. The UI could get painful if you can only go back one screen at a time.
My app requires an interface that has many buttons, text fields and matrixes. And they need to change from time to time. Right now I do this by having all elements in IB already and hiding/showing/moving them when needed. What would others recommend? Should I do that? Should I use an NSTabView? NSView? Should create the elements programatically? If so, what if I have an element that is already created that I need again without changes? It would be a waste of releasing it and creating it again.
Any help would be greatly appreciated.
In my opinion, it's better to create interfaces programmatically if you have to animate views around a lot. If it's just a matter of hiding/unhiding them, IB works great, but if you need re-layout or create unknown numbers of views dynamically it's not worth trying to make it all work with nib files.
As for general advice:
Create subclasses (from UIView or UIControl or one of their subclasses) for every kind of element you're going to use. It's tempting to piece together composite views from your UIViewController, but you'll really be much better off creating real classes.
Study the standard Cocoa view classes, and try to create similar API:s in your own controls and views.
Put as much data (sub-element positioning etc) into a plist, so that you can easily change it from one centralized place instead of having to dig around in the code.
If you are often creating several dozen short-lived views, it's worth keeping them in a pool and reusing them. But if it's just a few labels being added and removed intermittently I wouldn't worry too much about it. As usual: don't optimize too early.
Your current approach sounds fine. If you're showing/hiding them but otherwise they remain unchanged, why go through the trouble of creating them with code, when your XIB keeps a "freeze-dried" copy of exactly what you need already?
As long as you're keeping them within logical groups, you can just move/swap/show/hide the group's container (like NSBox or an NSView). If you have a LOT of logical groups, which aren't always shown every session, you can separate them out into their own XIBs and only load them when they're needed, to save launch time and memory.
If you use NSViewController, it's even better because you can make clean breaks for each logical group. Load the panel as the view and the view controller will keep outlets/actions and has a one-to-one relationship with a xib.