1

我的 iPhone 应用程序出现问题,当我在 iPod 上运行它时,它需要超过 10 分钟才会崩溃并关闭。因此,我查找了配置文件统计信息,并且似乎是高 Live Bytes,如下图所示:

实时字节

任何人都可以帮助我减少此 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];
}
4

2 回答 2

4

鉴于修改后的问题,您正在将图像从持久存储加载到(大概)一系列滚动视图中,有几个想法:

  1. 有多少张图片?

    如果每个只有 5 或 6 个,那么追求无限滚动课程是徒劳的。如果有 50+,那么无限滚动是非常重要的模式。(谷歌“UIImageView 无限滚动”。)这是我原始答案中“延迟加载”讨论的变体,但面向滚动视图的独特情况。

  2. 图片有多大?

    它们已经是 60x60(或者可能,对于视网膜,120x120)?如果它们比这大得多,您可能希望将它们调整为适合您的 UI 的最佳尺寸。您可以对应用程序包中的原始资源执行此操作,也可以通过编程方式调整它们的大小。

  3. 当您的应用程序崩溃时,您会看到什么异常/错误日志?

    您能否准确确认崩溃日志和/或错误消息、异常代码等的详细信息?

    • 虽然它可能看起来很神秘,但这些信息在诊断问题方面是非常宝贵的。我们真的想确保这段viewDidLoad代码确实是问题所在,如果是,那么错误究竟是什么。

    • 如果您还没有,我可能会打开一个异常断点(继续为“所有”异常执行此操作)。有时它可以帮助突出显示有问题的代码行(如果问题不仅仅是内存不足或被看门狗进程杀死)。

请继续使用相关详细信息更新您的问题。


原始答案(在看到viewDidLoad代码之前):

我鼓励您追求图像的“延迟加载”,仅在 UI 需要它们时加载它们,并在不再需要它们时释放它们(或删除对它们的所有强引用)(即有问题的图像滚出屏幕)。我会非常警惕在viewDidLoad. 它浪费了宝贵的内存,如果您同步执行此操作,您的应用程序甚至可能被看门狗进程杀死,因为您的应用程序始终应该在主队列中响应。0x8badf00d如果您收到异常代码(极客幽默:看门狗报告“吃了不好的食物”;有关一些异常代码的描述,请参阅TN2151 ),您可以判断看门狗是否会杀死您的应用程序。

最简单的解决方案是使用或中的UIImageView类别。如果您使用or ,这非常简单。如果您使用的是,则需要更多的工作(除非您使用第三方“无限滚动”类)。SDWebImageAFNetworkingUITableViewUICollectionViewUIScrollView

图像会占用大量内存,因此请仅在任何给定时刻保留 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应该创建控件,而只是填充支持您的 UI 的模型数组。然后,您的滚动视图将调用委托方法 ,该方法将根据需要加载和卸载图像。如果你有成千上万的图像,我可能会建议其他优化,但希望这能说明这个概念。UIButtonscrollViewDidScroll

于 2013-06-25T18:17:31.507 回答
0

您是使用 ARC 开发还是自己分配内存。尝试在对象过时时释放它们。内存分配不是那么高。您是否已锁定崩溃日志?在此处查看如何使用这些日志...

于 2013-06-25T16:48:05.863 回答