UIScrollView 与 UIImageView、UIImage
我在这里或其他谷歌搜索页面阅读了很多 UIScrollView 和 UIImageView 线程。但我仍然无法解决我面临的问题。我现在感冒了。希望我还能说清楚,哈哈。问题是:
我正在构建一个主要使用 UIScrollView 来显示一些图像的应用程序。这里重要的是数量,而不是大小,平均为 100KB(我什至将 PNG 转换为 jpg,不确定是否有帮助)。由于图像不超过 10 个,我的应用程序崩溃并出现内存警告。这是我第一次遇到内存问题,这让我很惊讶,因为编译后的应用程序还不到 10MB。
一开始,我在启动时加载所有图像,循环图像文件的所有名称,然后执行
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
[scrollview addSubview:imageView];
[imageView release];
如果我是对的,我认为启动后,所有图像都在内存中,对吧?但有趣的是,应用程序可以毫无问题地启动(最多出现 1 级内存警告)。当我滚动一些图像后,它崩溃了。我确实检查了泄漏以及分配情况。滚动过程中没有泄漏,分配几乎没有变化。
那么,imageNamed 是否做了一些特殊的事情而不是缓存?
然后,是的,我转向了延迟加载。
由于担心检查页面和按需加载图像可能会导致滚动(这被证明是正确的),我使用了一个运行循环的线程来检查滚动视图的偏移并加载/卸载图像。
我通过记住图像名称来对 UIImageView 进行子类化。它还包含将在该线程上执行的 loadImage 和 unloadImage。
- (void)loadImage {
/if ([self.subviews count] == 0) {
UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
[self performSelectorOnMainThread:@selector(renderImage:) withObject:iv waitUntilDone:NO];
//[self addSubview:iv];
[iv release];
}*/
if (self.image == nil) {
//UIImage *img = [UIImage imageNamed:self.imageName];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
// image must be set on main thread as UI rendering is main thread's responsibility
[self performSelectorOnMainThread:@selector(renderImage:) withObject:img waitUntilDone:NO];
[img release];
}
}
// 在主线程上渲染图像
- (void)renderImage:(UIImage*)iv {
//[self addSubview:iv];
self.image = iv;
}
- (void)unloadImage {
self.image = nil;
//[(UIView*)[self.subviewslastObject]removeFromSuperview];
}
你可以看到我玩过的注释代码。
在unloadImage中,如果我写[self.image release]
,那么我会得到EXC_BAD_ACCESS,这是意料之外的,因为我认为alloc和release在这里是匹配的。
该应用程序仍然崩溃,没有泄漏。 initWithContentsOfFile
版本甚至比 imageNamed 版本更早崩溃,导致滚动不那么流畅。
我在设备上运行该应用程序。通过检查分配情况,我发现 imageNamed
版本使用的内存比 initWithContentsOfFile
版本少得多,尽管它们都崩溃了。 Instruments 还显示分配的图像是 2,3 或 4,这表明延迟加载确实完成了他的工作。
我检查了WWDC2010的PhotoScroller,但我认为它没有解决我的问题。不涉及缩放或巨大的图片。
任何人都可以帮忙!先感谢您。
I've read a lot of UIScrollView with UIImageView threads here or other googled pages. But I still cannot get the problem I'm confronting. I'm having a cold right now. Hope I can still make it clear, lol. Here is the problem:
I'm building one app which mainly uses UIScrollView to display a few images. Here the amount counts, not the size, which is averagely 100KB(I even converted PNG to jpg, not sure whether it helps or not). With no more than 10 images, my app crashes with memory warning. This is the first time I encounter memory issue, which surprised me as the compiled app is less than 10MB.
At the very beginning, I load all the images on launch, looping all names of image files and do
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
[scrollview addSubview:imageView];
[imageView release];
If I'm right, I think after launch, all the images are in memory, right? But the funny thing here is, the app could launch without any problem(at most a level 1 memory warning). After I scroll a few images, it crashed. I checked leaks for sure and also allocations. No leak and allocation almost had no change during scrolling.
So, is there something special done by imageNamed rather than cache?
And then, yes, I turned to lazy load.
For fear of checking page and loading images on demand might jerk the scrolling(which was proved true), I used a thread which runs a loop to check offset of the scroll view and load/unload images.
I subclassed UIImageView by remembering the image name. It also contains loadImage and unloadImage which will be executed on that thread.
- (void)loadImage {
/if ([self.subviews count] == 0) {
UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
[self performSelectorOnMainThread:@selector(renderImage:) withObject:iv waitUntilDone:NO];
//[self addSubview:iv];
[iv release];
}*/
if (self.image == nil) {
//UIImage *img = [UIImage imageNamed:self.imageName];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
// image must be set on main thread as UI rendering is main thread's responsibility
[self performSelectorOnMainThread:@selector(renderImage:) withObject:img waitUntilDone:NO];
[img release];
}
}
// render image on main thread
- (void)renderImage:(UIImage*)iv {
//[self addSubview:iv];
self.image = iv;
}
- (void)unloadImage {
self.image = nil;
//[(UIView*)[self.subviews lastObject] removeFromSuperview];
}
You can see the commented code that I've played with.
In unloadImage, if I write [self.image release]
, then I get EXC_BAD_ACCESS, which is unexpected, as I think alloc and release are matched here.
The app still crashes with no leak. The initWithContentsOfFile
version even crashed earlier than imageNamed version, and made the scrolling not that smooth.
I run the app on device. By checking allocations, I found imageNamed
version used much less memory than initWithContentsOfFile
version, though they both crash. Instruments also showed that the allocated images were 2,3 or 4, which indicated the lazy load did do his job.
I checked PhotoScroller of WWDC2010, but I don't think it solvs my problem. There is no zooming or huge picture involved.
Anybody helps! Thank you in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
崩溃日志什么也没说。应用程序崩溃大多是在内存警告级别= 2之后。如果在模拟器上运行,就不会有问题。
The crash log says nothing. The app crashes mostly after memory warning level = 2. And if run on simulator, there will be no problem.
您的图像使用哪种格式并不重要。当您显示它们时,它们会转换为位图。
我建议使用类似于 UITableView 使用的技术(隐藏图像并释放它从屏幕上消失时使用的内存,并仅在需要显示图像时实例化图像)。
作为替代方法 - 如果您需要在网格中显示这些图像 - 您可以看看 CATiledLayer。
无论如何,将所有图像加载到内存中并不是最好的主意:)
It doesn't matter which format do you use for your images. They're converted to bitmaps when you display them.
I'd suggest to use the technique similar to that one which is used by UITableView (hide the image and free the memory it uses when it disappears from the screen and instantiate the image only when you need to show it).
As an alternate way – if you need to show these images in a grid – you might take a look to a CATiledLayer.
Anyhow, loading all the images to the memory is not the best idea :)
您可以将所有图像加载到数组中。您可以设计一个具有一个图像视图的视图,并尝试以下代码:
数组名称:examplearray 和视图名称:exampleview
You can load all the images to an array. And you can design a view having one image view and try the below code:
array name: examplearray and view name :exampleview