由于巨大的UILabel,应用程序内存不足

时间:2014-11-13 11:13:08

标签: ios xcode memory core-animation didreceivememorywarning

我必须在我的应用中显示一个巨大的文本文件。 UITextView不符合我的要求,因为它强制换行,所以我不得不使用UILabel。由于非常大的标签不会被渲染,我在UILabels内使用了多个UIScrollView来使其正常工作。

一切都在模拟器上运行,但UILabels所需的内存大约为300MB。当我在iPad 2上运行时,内存不足,应用程序崩溃。

问题在于我没有得到任何内存警告。我想解除didReceiveMemoryWarning中的视图控制器,但它没有被调用,应用程序崩溃而没有任何警告。

我错过了什么?

1 个答案:

答案 0 :(得分:1)

以下是使用UITableView解决问题的示例。

<强> HSViewController.h

@interface HSViewController : UITableViewController

@end

<强> HSViewController.m

#import "HSViewController.h"

//#define USE_LABEL

static NSString *const kCellIdentifier = @"kCellIdentifier";

@interface HSViewController ()

@property (atomic, strong) NSArray *linesOfText;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;

- (CGSize)sizeForString:(NSString *)text;

@end

@implementation HSViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

    self.font = [UIFont systemFontOfSize:14.0f];
    self.lineBreakMode = NSLineBreakByWordWrapping;

    [self.tableView registerClass:[UITableViewCell class]
           forCellReuseIdentifier:kCellIdentifier];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *draculaData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"Dracula" withExtension:@"txt"]];
        NSString *text = [[NSString alloc] initWithData:draculaData encoding:NSASCIIStringEncoding];
#ifndef USE_LABEL
        self.linesOfText = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
#endif
        dispatch_async(dispatch_get_main_queue(), ^{
#ifdef USE_LABEL
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f,
                                                                       self.view.bounds.size.width, self.view.bounds.size.height)];
            label.font = self.font;
            label.lineBreakMode = self.lineBreakMode;
            label.numberOfLines = 0;
            label.text = text;
            [self.view addSubview:label];
#else
            NSLog(@"Starting reloading %lu rows", (unsigned long)[self.linesOfText count]);
            [self.tableView reloadData];
            NSLog(@"Reload finished");
#endif
        });
    });
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#ifdef USE_LABEL
    return 0;
#else
    return [self.linesOfText count];
#endif
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    @autoreleasepool {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier forIndexPath:indexPath];
        cell.textLabel.numberOfLines = 0;
        cell.textLabel.font = self.font;
        cell.textLabel.lineBreakMode = self.lineBreakMode;
        cell.textLabel.text = self.linesOfText[indexPath.row];
        cell.userInteractionEnabled = NO;
        return cell;
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    @autoreleasepool {
        NSString *text = self.linesOfText[indexPath.row];
        // Don't let the line height be 0
        if ([text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0)
        {
            text = @"A";
        }
        return ceil([self sizeForString:text].height);
    }
}

#pragma mark - Private

- (CGSize)sizeForString:(NSString *)text
{
    @autoreleasepool {
        if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
        {
            NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
            style.lineBreakMode = self.lineBreakMode;
            return [text boundingRectWithSize:CGSizeMake(self.tableView.bounds.size.width, CGFLOAT_MAX)
                                      options:NSStringDrawingUsesLineFragmentOrigin
                                   attributes:@{NSFontAttributeName : self.font,
                                                NSParagraphStyleAttributeName : [style copy]}
                                      context:[NSStringDrawingContext new]].size;
        }
        else
        {
            return [text sizeWithFont:self.font
                    constrainedToSize:CGSizeMake(self.tableView.bounds.size.width, CGFLOAT_MAX)
                        lineBreakMode:self.lineBreakMode];
        }
    }
}

@end

您会发现内存使用率实际上没有太大差异。 UITableView版本仅保存约。内存占10%。

但是,这是动态加载表格视图单元格的起点。因此,当用户向下滚动(让屏幕显示为90%)时,可以通过更改tableView:numberOfRowsInSection:的返回值来加载下一个X单元格。

祝你好运。