UIImage 导致整个 iPhone 的视图变黑
好吧,这个问题相当奇怪,目前我 99% 肯定这不是保留/释放错误。基本上我使用 UIImagePickerController 从库或相机中获取图像,但生成的图像会导致一些......奇怪的事情发生。虽然在包含 UIImageView 的详细视图控制器中拉出图像很滞后,但真正的问题是,如果我重复拉起视图控制器并关闭它,最终图像的一部分将会消失!更令人不安的是,如果我在发生这种情况后尝试再次将其拉起,整个图像将变黑,并且我的应用程序和 iPhone 本身中的其他 UIView 要么剧烈闪烁,要么变黑!例如,iPhone壁纸和最初的“滑动解锁”面板分别变黑和闪烁……无论照片来自图库还是相机,都没有区别。如果我这样做,这个问题就完全避免了(没有滞后,没有黑度):
//UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
@"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
因此我无法想象这个问题与 UIImagePickerController 以外的任何东西有关。请注意,我仍在创建和消除它——我只是没有从中获取图像。
起初我以为图像只是被过度释放了,消失的黑色块是记忆被覆盖的一部分。但是,更改图像的保留计数没有任何区别。然后我想,也许图像太大,当我关闭 UIImageView 时,不知何故无法正确释放!然而,从互联网下载几个 MB 的图像不会复制该错误。然后我想,也许 UIImagePickerController 正在处理图像,无论保留计数如何!但复制图像也失败了。此外,这些事情怎么可能影响存在于 iOS 级别深处的视图呢?!我进行了研究、实验和谷歌搜索,除了我之外没有人遇到过这个问题......但我并没有做得特别奇怪!这是苹果的问题吗?我忘记了一些明显的事情吗?我已经上下扫描了文档和指南,但无济于事,但也许我错过了一些东西。
这些都不起作用:
- 增加保留计数
- 使用 [图像复制]
这些都没有复制下载图像的问题:
- 减少保留计数
- 下载大小大于 1 MB 的大尺寸图像
我正在使用最新的 Verizon装有 iOS 4.2.8 的 iPhone(基础 SDK“覆盖”到 4.3,无论这意味着什么)。 4.2.8 是 Verizon 的最新版本,尽管 4.3 适用于使用 AT&T 的 iPhone。
这是非常详细的代码。我还没有检查设备兼容性,但这应该不重要。也许我忘记了 UIImagePickerController 的一些设置?
快速概述:我显示一个操作表,然后根据用户的输入显示图像选择器。我使用自定义转换器将图像保存为核心数据对象(交付)上的可转换属性。随后,我将图像交给详细视图控制器以显示给用户。
IGDeliveryVC.m(无论如何,它的一部分。它是一个显示交付添加的媒体的表格视图)
- (void)refresh
{
[mediaDisplayArray release];
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"displayIndex" ascending:YES];
mediaDisplayArray = [[NSMutableArray alloc] initWithArray:
[delivery.deliveryMedia sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]]];
if (mediaDisplayArray == nil)
mediaDisplayArray = [[NSMutableArray alloc] init];
[self.tableView reloadData];
}
- (void)onAddMedia:(id)sender
{
#if TARGET_IPHONE_SIMULATOR
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
@"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
[self refresh];
[image release];
return;
#endif
UIActionSheet *options = [[UIActionSheet alloc] initWithTitle:@"Add Media from..."
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Camera", @"Library", nil];
[options showFromToolbar:self.navigationController.toolbar];
[options release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0 && buttonIndex != 1)
return;
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.mediaTypes = [NSArray arrayWithObjects:@"public.image",@"public.movie", nil];
switch (buttonIndex)
{
case 0:
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
break;
case 1:
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
break;
}
[self presentModalViewController:picker animated:YES];
}
- (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker
{
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString* mediaType = (NSString*)[info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:@"public.image"])
{
UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
//UIImage* image = [[UIImage alloc] initWithData:
// [NSData dataWithContentsOfURL:
// [NSURL URLWithString:
// @"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
}
else if ([mediaType isEqualToString:@"public.movie"])
{
NSString* videoURL = (NSString*)[info objectForKey:UIImagePickerControllerMediaURL];
[delivery addVideo:videoURL];
}
else
{
NSLog(@"Error: imagePickerController returned with unexpected type %@", mediaType);
}
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
IGMedia* media = [mediaDisplayArray objectAtIndex:indexPath.row];
UIViewController* detailViewController =
[media isMemberOfClass:[IGMediaImage class]]
? [[IGMediaImageDetailVC alloc] initWithImage:((IGMediaImage*)media).image]
: nil;
if (detailViewController != nil)
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
IGMediaImageDetailVC.h(我使用 xib :P)
#import <UIKit/UIKit.h>
@interface IGMediaImageDetailVC : UIViewController {
}
@property (nonatomic, retain) UIImage* image;
@property (nonatomic, retain) IBOutlet UIImageView* imageView;
- (id)initWithImage:(UIImage*)anImage;
@end
IGMediaImageDetailVC.m
#import "IGMediaImageDetailVC.h"
@implementation IGMediaImageDetailVC
@synthesize image;
@synthesize imageView;
- (id)initWithImage:(UIImage*)anImage
{
self = [super initWithNibName:@"IGMediaImageDetailVC" bundle:nil];
if (self)
{
self.image = anImage;
}
return self;
}
- (void)dealloc
{
[image release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView.image = self.image;
}
- (void)viewDidUnload
{
[super viewDidUnload];
[imageView release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
如果我可以做些什么来使这篇文章更清晰,请告诉我。我会将不起作用/重复问题的内容添加到适当的列表中。感谢您花时间阅读本文!
Alright, this problem is rather bizarre, and I'm 99% positive at this point it's not a retain/release error. Basically I'm using UIImagePickerController to grab an image from either the library or the camera, but the resulting image causes some... strange things to happen. Though it's laggy to pull the image up in a detail view controller containing a UIImageView, the real problem is that if I repetitively pull up the view controller and dismiss it, eventually parts of the image will have disappeared!!! More disturbingly, if I attempt to pull it up again after this has happened, the whole image will have turned black, and other UIViews throughout both my App AND the iPhone itself will either flicker wildly or have turned black!!!! For example, both the iPhone wallpaper and the initial "slide to unlock" panel turn black and flicker, respectively... It makes no difference whether the photo came from the library or the camera. The problem is entirely averted (no lag, no blackness) if I do something like this:
//UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
@"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
Thus I can't imagine the problem having to do with anything other than the UIImagePickerController. Note that I'm still creating and dismissing it-- I'm just not grabbing the image out of it.
At first I thought the image was simply being overreleased, and the disappearing black chunks were parts of memory being overwritten. However, changing the image's retain count makes no difference. Then I thought, maybe the image is too large and somehow not getting properly released when I dismiss the UIImageView! Yet downloading a several MB image from the internet will not replicate the error. Then I thought, maybe the UIImagePickerController is disposing of the image, regardless of retain count! But copying the image failed as well. Furthermore, how could any of these things effect views that exist as deep as the iOS level?! I've researched, experimented, and Googled and no one has encountered this problem except for me... yet I'm not doing particularly strange! Is this an Apple issue? Did I forget something obvious? I've scanned up and down the documentation and guides to no avail, but perhaps I missed something.
None of this has worked:
- Incrementing the retain count
- Using [image copy]
None of this has replicated the problem with the downloaded image:
- Decrementing the retain count
- Downloading an image of size greater than 1 MB with large dimensions
I'm using the latest Verizon iPhone with iOS 4.2.8 (base SDK "overriden" to 4.3, whatever that means). 4.2.8 is the latest possible one for Verizon, though 4.3 is available for iPhones using AT&T.
Here's the code in glorious detail. I'm not yet checking for device compatibility, but it shouldn't matter concerning this. Perhaps I forgot some setting with the UIImagePickerController?
Quick Overview: I display an action sheet, then based on the user's input, display the image picker. I save the image as a transformable attribute on a Core Data object (delivery) using a custom transformer. I later hand the image to a detail view controller to display to the user.
IGDeliveryVC.m (parts of it, anyways. It's a tableview displaying the delivery's added media)
- (void)refresh
{
[mediaDisplayArray release];
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"displayIndex" ascending:YES];
mediaDisplayArray = [[NSMutableArray alloc] initWithArray:
[delivery.deliveryMedia sortedArrayUsingDescriptors:
[NSArray arrayWithObject:sortDescriptor]]];
if (mediaDisplayArray == nil)
mediaDisplayArray = [[NSMutableArray alloc] init];
[self.tableView reloadData];
}
- (void)onAddMedia:(id)sender
{
#if TARGET_IPHONE_SIMULATOR
UIImage* image = [[UIImage alloc] initWithData:
[NSData dataWithContentsOfURL:
[NSURL URLWithString:
@"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
[self refresh];
[image release];
return;
#endif
UIActionSheet *options = [[UIActionSheet alloc] initWithTitle:@"Add Media from..."
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Camera", @"Library", nil];
[options showFromToolbar:self.navigationController.toolbar];
[options release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0 && buttonIndex != 1)
return;
UIImagePickerController* picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = NO;
picker.mediaTypes = [NSArray arrayWithObjects:@"public.image",@"public.movie", nil];
switch (buttonIndex)
{
case 0:
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
break;
case 1:
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
break;
}
[self presentModalViewController:picker animated:YES];
}
- (void) imagePickerControllerDidCancel: (UIImagePickerController *) picker
{
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSString* mediaType = (NSString*)[info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:@"public.image"])
{
UIImage* image = (UIImage*)[info objectForKey:UIImagePickerControllerOriginalImage];
//UIImage* image = [[UIImage alloc] initWithData:
// [NSData dataWithContentsOfURL:
// [NSURL URLWithString:
// @"http://wild-facts.com/wp-content/uploads/2010/07/Diving_emperor_penguin.jpg"]]];
[delivery addImage:image];
}
else if ([mediaType isEqualToString:@"public.movie"])
{
NSString* videoURL = (NSString*)[info objectForKey:UIImagePickerControllerMediaURL];
[delivery addVideo:videoURL];
}
else
{
NSLog(@"Error: imagePickerController returned with unexpected type %@", mediaType);
}
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
[picker release];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
IGMedia* media = [mediaDisplayArray objectAtIndex:indexPath.row];
UIViewController* detailViewController =
[media isMemberOfClass:[IGMediaImage class]]
? [[IGMediaImageDetailVC alloc] initWithImage:((IGMediaImage*)media).image]
: nil;
if (detailViewController != nil)
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
IGMediaImageDetailVC.h (I use a xib :P )
#import <UIKit/UIKit.h>
@interface IGMediaImageDetailVC : UIViewController {
}
@property (nonatomic, retain) UIImage* image;
@property (nonatomic, retain) IBOutlet UIImageView* imageView;
- (id)initWithImage:(UIImage*)anImage;
@end
IGMediaImageDetailVC.m
#import "IGMediaImageDetailVC.h"
@implementation IGMediaImageDetailVC
@synthesize image;
@synthesize imageView;
- (id)initWithImage:(UIImage*)anImage
{
self = [super initWithNibName:@"IGMediaImageDetailVC" bundle:nil];
if (self)
{
self.image = anImage;
}
return self;
}
- (void)dealloc
{
[image release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView.image = self.image;
}
- (void)viewDidUnload
{
[super viewDidUnload];
[imageView release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
If there's anything I can do to make this post more legible, please let me know. I'll add things that don't work/replicate the problem to the appropriate list. Thanks for taking the time to read this!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我刚刚弄清楚问题所在,与内存使用过多有关。感谢这篇精彩的文章: https://stackoverflow.com/questions/1282830/uiimagepickercontroller-uiimage -memory-and-more 基本上,在显示图像之前,我将每个维度除以 4,结果总共是我之前使用的内存的 1/16。这是我使用的方法(根据很棒的帖子):
在方法调用中,我传入
CGSizeMake(image.size.width/4, image.size.height/4)
希望这有帮助!
I just figured out the problem, had to do with excessive memory use. Thanks goes to this amazing post: https://stackoverflow.com/questions/1282830/uiimagepickercontroller-uiimage-memory-and-more Basically, before ever displaying the image, I divide each dimension by 4, resulting in a total 1/16th of the memory I was using before. Here's the method I used (as per the awesome post):
In the method call, I pass in
CGSizeMake(image.size.width/4, image.size.height/4)
Hope this was helpful!