如何减少iOS中的“Live Bytes”?

时间:2013-06-25 16:44:17

标签: iphone ios objective-c byte

我的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];
}

2 个答案:

答案 0 :(得分:4)

鉴于修订后的问题,您将持久存储中的图像加载到(可能是)一系列滚动视图中,但有几点想法:

  1. 有多少张图片?

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

  2. 图片有多大?

    它们已经是60x60(或者可能是视网膜,120x120)?如果它们比这大得多,您可能希望将它们调整为适合UI的最佳大小。您可以在应用套装中的原始资源上执行此操作,也可以programmatically resize them进行此操作。

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

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

    • 虽然它看起来很神秘,但就诊断问题而言,这些信息非常宝贵。我们确实希望确保这个viewDidLoad代码实际上是问题,如果是,那么错误到底是什么。

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

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


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

    我鼓励你继续追求“延迟加载”图片,只有当UI需要它们时加载它们,并在不再需要它们时释放它们(或删除所有对它们的强引用) (即有问题的图像从屏幕滚出)。我会非常谨慎地在viewDidLoad的循环中加载图像。它浪费了宝贵的内存,如果你同步这样做,你的应用程序甚至可能被看门狗进程杀死,因为你的应用程序总是应该在主队列中响应。如果你得到0x8badf00d的异常代码,你可以判断看门狗是否会杀死你的应用程序(极客幽默:看门狗正在报告“吃不好的食物”;有关几个异常代码的说明,请参阅TN2151 )。

    最简单的解决方案是在SDWebImageAFNetworking中使用UIImageView类别。如果您使用的是UITableViewUICollectionView,这非常简单。如果您使用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如何使用这些日志......