我的iPhone应用程序出现问题,当我在iPod上运行时,它需要超过10分钟才能崩溃并关闭。 所以我查找了个人资料统计信息,并且似乎是高字节字节,如下图所示:
任何人都可以帮我减少这个Live Bytes以避免崩溃吗?
修改 我在调用viewDidLoad时使用了三个for循环,并且三个循环从引用中获取图像,所以我认为它会让应用程序崩溃? 那么在不造成崩溃的情况下,做这三个for循环的替代方法是什么?
编辑2: 这是我的视图加载,每个循环从不同的文件夹获取不同的图像,并且所有这些都是必要的
编辑3:对于文件夹a,它有100个图像,其中一半是每个图像的缩略图,一个是文件夹b,30个图像,最后是文件夹c 90个图像,其中一半是缩略图对于每个图像,所有文件夹都在引用中,因此它没有从互联网获取任何图像...
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSInteger x = 6;
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *aPath = [resourcePath stringByAppendingPathComponent:@"a"];
NSArray *aDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:aPath error:nil];
for (NSString *tString in aDirContents) {
if ([tString hasSuffix:@".jpg"]) {
UIButton *item = [UIButton buttonWithType:UIButtonTypeCustom];
[item setFrame:CGRectMake(x, 5, 60, 60)];
x += 63;
NSString *result = [tString substringToIndex:[tString length] - 4];
[item setTitle:result forState:UIControlStateNormal];
[item setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
[item setBackgroundImage:[UIImage imageNamed:tString] forState:UIControlStateNormal];
[item addTarget:self action:@selector(changeFont:) forControlEvents:UIControlEventTouchUpInside];
[aSlider addSubview:item];
}
}
aSlider.frame = CGRectMake(aSlider.frame.origin.x, aSlider.frame.origin.y, x, 69);
[aSliderContainer setContentSize:aSlider.frame.size];
x = 6;
NSString *bPath = [resourcePath stringByAppendingPathComponent:@"b"];
NSArray *bDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bPath error:nil];
for (NSString *tString in bDirContents) {
if ([tString hasSuffix:@".jpg"]) {
UIButton *item = [UIButton buttonWithType:UIButtonTypeCustom];
[item setFrame:CGRectMake(x, 5, 60, 60)];
x += 63;
NSString *result = [tString substringToIndex:[tString length] - 4];
[item setTitle:result forState:UIControlStateNormal];
[item setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
[item setBackgroundImage:[UIImage imageNamed:tString] forState:UIControlStateNormal];
[item addTarget:self action:@selector(changeFont:) forControlEvents:UIControlEventTouchUpInside];
[bSlider addSubview:item];
}
}
bSlider.frame = CGRectMake(bSlider.frame.origin.x, bSlider.frame.origin.y, x, 69);
[bSliderContainer setContentSize:bSlider.frame.size];
// c Slider
x = 6;
NSString *cPath = [resourcePath stringByAppendingPathComponent:@"c"];
NSArray *cDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:cPath error:nil];
for (NSString *tString in cDirContents) {
if ([tString hasSuffix:@".jpg"]) {
UIButton *item = [UIButton buttonWithType:UIButtonTypeCustom];
[item setFrame:CGRectMake(x, 5, 60, 60)];
x += 63;
NSString *result = [tString substringToIndex:[tString length] - 4];
[item setTitle:result forState:UIControlStateNormal];
[item setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
[item setBackgroundImage:[UIImage imageNamed:tString] forState:UIControlStateNormal];
[item addTarget:self action:@selector(changeFont:) forControlEvents:UIControlEventTouchUpInside];
[cSlider addSubview:item];
}
}
cSlider.frame = CGRectMake(cSlider.frame.origin.x, cSlider.frame.origin.y, x, 69);
[cSliderContainer setContentSize:cSlider.frame.size];
}
答案 0 :(得分:4)
鉴于修订后的问题,您将持久存储中的图像加载到(可能是)一系列滚动视图中,但有几点想法:
有多少张图片?
如果每个只有5个或6个,那么追求无限滚动课是徒劳的。如果有50+,那么无限滚动是非常重要的模式。 (Google“UIImageView无限滚动”。)这是我原始答案中“延迟加载”讨论的变体,但是面向滚动视图的独特情况。
图片有多大?
它们已经是60x60(或者可能是视网膜,120x120)?如果它们比这大得多,您可能希望将它们调整为适合UI的最佳大小。您可以在应用套装中的原始资源上执行此操作,也可以programmatically resize them进行此操作。
当您的应用崩溃时,您会看到什么异常/错误日志?
您能否准确确认崩溃日志和/或错误消息,异常代码等的详细信息。
虽然它看起来很神秘,但就诊断问题而言,这些信息非常宝贵。我们确实希望确保这个viewDidLoad
代码实际上是问题,如果是,那么错误到底是什么。
如果您还没有,我可能会打开exception breakpoint(继续为“所有”例外执行此操作)。有时它可以帮助突出显示有问题的代码行(如果问题不仅仅是内存耗尽或被看门狗进程杀死)。
请继续使用相关详细信息更新您的问题。
原始答案(在查看viewDidLoad
代码之前):
我鼓励你继续追求“延迟加载”图片,只有当UI需要它们时加载它们,并在不再需要它们时释放它们(或删除所有对它们的强引用) (即有问题的图像从屏幕滚出)。我会非常谨慎地在viewDidLoad
的循环中加载图像。它浪费了宝贵的内存,如果你同步这样做,你的应用程序甚至可能被看门狗进程杀死,因为你的应用程序总是应该在主队列中响应。如果你得到0x8badf00d
的异常代码,你可以判断看门狗是否会杀死你的应用程序(极客幽默:看门狗正在报告“吃不好的食物”;有关几个异常代码的说明,请参阅TN2151 )。
最简单的解决方案是在SDWebImage
或AFNetworking
中使用UIImageView
类别。如果您使用的是UITableView
或UICollectionView
,这非常简单。如果您使用UIScrollView
,则需要更多工作(除非您使用第三方“无限滚动”类)。
图像占用大量内存,因此只能在任何给定时刻保留UI所需的内容,并且不会不必要地“预取”图像。我鼓励你谷歌“UIImage延迟加载”一词,你会得到大量的点击。
如果你想做一个无限卷轴,为每个InfiniteScrollerButton定义一个模型对象:
@interface InfiniteScrollerButton : NSObject
@property (nonatomic, copy, readonly) NSString *filename; // what is the full path of the image
@property (nonatomic, weak, readonly) UIButton *button; // the UIButton; nil if no button yet created for this icon
@property (nonatomic, readonly) CGRect frame; // the frame that the icon does (or would) occupy
@property (nonatomic, readonly) NSInteger tag; // the tag number for the button
- (id)initWithFilename:(NSString *)filename index:(NSInteger)index;
- (void)addButtonToView:(UIView *)view target:(id)target action:(SEL)action;
- (void)removeButton;
@end
实现可能如下:
@interface InfiniteScrollerButton ()
@property (nonatomic, copy) NSString *filename;
@property (nonatomic, weak) UIButton *button;
@property (nonatomic) CGRect frame;
@property (nonatomic) NSInteger tag;
@end
@implementation InfiniteScrollerButton
- (id)initWithFilename:(NSString *)filename index:(NSInteger)index
{
self = [super init];
if (self) {
_filename = [filename copy];
_frame = CGRectMake(6 + index * 63, 5, 60, 60);
_tag = index;
}
return self;
}
- (void)addButtonToView:(UIView *)view target:(id)target action:(SEL)action
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
self.button = button;
// on old devices, even retrieving an image from persistent storage can affect the smoothness
// of the UI, so let's do that asynchronously
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *image = [UIImage imageWithContentsOfFile:self.filename];
dispatch_async(dispatch_get_main_queue(), ^{
button.frame = self.frame;
button.tag = self.tag;
NSString *tString = [self.filename lastPathComponent];
NSString *result = [tString substringToIndex:[tString length] - 4];
[button setTitle:result forState:UIControlStateNormal];
[button setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
[button setBackgroundImage:image forState:UIControlStateNormal];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[view addSubview:button];
});
});
}
- (void)removeButton
{
if (self.button)
{
[self.button removeFromSuperview];
self.button = nil;
}
}
@end
然后您的视图控制器可能如下所示:
@interface ViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) NSMutableArray *icons;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadModel];
[self scrollViewDidScroll:self.scrollView]; // call this once, manually, so the initial load of visible images takes place
}
// load the model backing our scrollview
- (void)loadModel
{
self.icons = [NSMutableArray array];
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *aPath = [resourcePath stringByAppendingPathComponent:@"a"];
NSArray *aDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:aPath error:nil];
[aDirContents enumerateObjectsUsingBlock:^(NSString *tString, NSUInteger idx, BOOL *stop) {
InfiniteScrollerButton *icon = [[InfiniteScrollerButton alloc] initWithFilename:[aPath stringByAppendingPathComponent:tString] index:idx];
[self.icons addObject:icon];
}];
InfiniteScrollerButton *lastObject = [self.icons lastObject];
self.scrollView.contentSize = CGSizeMake(lastObject.frame.origin.x + lastObject.frame.size.width + 6, 69);
}
- (void)changeFont:(id)sender
{
NSLog(@"%s", __FUNCTION__);
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
static NSInteger kMargin = 100;
// get the CGRect for the visible portion of the scroll view,
// adding a bit of a margin so it, effectively, will also load
// the next UIButton, too
CGRect contentRect = CGRectMake(scrollView.contentOffset.x - kMargin,
scrollView.contentOffset.y,
scrollView.bounds.size.width + kMargin * 2,
scrollView.bounds.size.height);
// iterate through all of the icons
for (InfiniteScrollerButton *icon in self.icons)
{
if (CGRectIntersectsRect(contentRect, icon.frame)) // if the icon should be visible ...
{
if (icon.button == nil) // ... but it's not, then add it
[icon addButtonToView:scrollView
target:self
action:@selector(changeFont:)];
}
else // if the icon is no longer visible ...
{
if (icon.button != nil) // ... but it exists, then remove it
[icon removeButton];
}
}
}
@end
只需确保将视图控制器指定为滚动视图的委托,即可开始比赛。显然,这只有一个滚动视图,但希望你能得到这个想法。您的viewDidLoad
应该不创建UIButton
控件,而只是填充支持UI的模型阵列。然后,您的滚动视图将调用委托方法scrollViewDidScroll
,该方法将根据需要加载和卸载图像。如果您有成千上万的图像,我可能会建议其他优化,但希望这说明了这个概念。
答案 1 :(得分:0)
您是使用ARC开发还是自己分配内存?尝试在对象过时时释放它们。内存分配不是那么高。你有没有锁定崩溃日志? 看看here如何使用这些日志......