开发者

Allocating objects inside a loop

Is it correct to allocate in a loop, in order to create multiple objects?

for (int i = 0; i < limit; i++)  
{  
 UILabel *lbl = [[UILabel alloc] initWithFrame:(CGRectMake(i*20, 0, 20, 20))];  
 lbl.textColor = [UIColor whiteColor];  
 lbl.font = [UIFont systemFontOfSize:16];  
 lbl.backgroundColor = [UIColor blackColor];  
 lbl.text = [NSString stringWithFo开发者_运维百科rmat: @"%d", i];  
 [self.view addSubview:lbl];
 [lbl release];  
}  

In my project, I create hundreds of small labels into several uiscrollview panels. And even though I am calling the release method on these labels, I get poor performance when gesturing: even after rasterizing views. Feels like all those labels are still there, waiting to be flushed from memory.

So my hint on the problem would be related with the way I allocate.

What am I missing?...


The memory won't be freed, because you add your UILabels to the view. Even if you call release, because the view is retaining them.


Until self.view is itself released, all of those labels do in fact continue to exist. Your code allocates memory for and initializes the object and then passes ownership of the object over to your view. Your release means that when the view is released you won't have retained ownership of them so the system will be free to get rid of them: your release itself doesn't get rid of the objects. (I've added an illustration below in case it's not clear.)

It's not just a memory issue, though, because a UILabel object has all kinds of other overhead: its own dozen properties and methods, the several dozen properties and methods it inherits from UIView, another dozen inherited from UIResponder, and more than another dozen from UIObject. That UILabel can do all kinds of nifty things as a result: have colors and shadows and alpha transparency and manage word wrap and have gesture recognizers attached and be animated and have its own subviews, etc.

For a much lighter solution you can draw the same text using NSString's drawInRect:withFont: method (which it gets from its UIKit Additions). None of that UILabel overhead, though none of its niceties, either. Still, with a little effort it should do exactly what you want.


This is likely overkill, but an illustration:

Allocating objects inside a loop

  1. Your ViewController allocates and initializes the View. There is one copy of the View in memory, and the ViewController retains ownership of the View until you (most likely) release the ViewController in its dealloc method (unless you manually release it before then).

  2. Your ViewController allocates and initializes a Label. There is one copy of the Label in memory, and the ViewController retains ownership of the Label.

  3. By calling the View's addView method you add ownership of the Label to the View (in addition to instructing it to display the Label). There is still one copy of the Label in memory and two objects now own it.

  4. By calling the Label's release method the ViewController gives up ownership of the Label. There is still one copy of the Label in memory and the View still owns it.

You repeat steps 2 - 4 every time through the loop, with the View retaining ownership of the allocated Label every time.

Each time the Application reaches the end of its run loop (which happens over and over while the app is running) if there are any allocated objects that have no owner then the memory used to store them is released. Because your View still owns all of those labels the memory you allocated for them continues to be used.


In your interface, how many labels are visible to the user on-screen at any given time? Since you're putting the labels in UIScrollViews, I'm assuming the user sees considerably less than the hundreds you are allocating and drawing at once. If the user can't seen hundreds of labels at once, why create them at once?

You should think about adopting a pattern similar to the UITableViewDataSource protocol. A UITableView doesn't alloc 500 UITableViewCells up front. It asks its dataSource for a cell only when it needs to be drawn. Additionally, it will recycle cells that go off-screen to offset the cost of alloc/init'ing a UITableViewCell from scratch. When properly configured, there will only ever be as many UITableViewCells in memory as the user can see on-screen.

So, think about designing your UIScrollView to request labels only as they need to be drawn, and then retain unused labels in memory when they are moved off-screen. This way you only ever have as many labels in memory as the user can possibly see on-screen.

There's some sample code that shows how to do this by subclassing UIScrollView:

ScrollViewSuite

Check out the "Tiling" project. It takes this concept a step further by introducing scale resolutions, but it should hopefully be enough to point you in the right direction.


if you are creating hundreds, it will be very expensive, and will be the route of the poor performance.

what you would probably be better doing is creating a reuse pool using a mutable array/set.

when you need one on screen, see if there is one in the set, and if not create one. then when it is scrolled off of screen, remove it from the superview, and add it to the reuse pool. like tableViewCells. this way you only have to create however many can be on screen at once.

hope this helps...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜