如何提高加载UITableViewController的速度

时间:2016-05-20 00:23:25

标签: ios objective-c uitableview

我有一个从后端填充的UITableViewController。 该单元格是一个自定义单元格,具有:

  • 1个UIImageView(带有clipBounds和roundcorners的圆圈)将在cellForRowAtIndexPath中异步加载。
  • 预装了3个UIImageViews(每个45px x 30px和700个字节)
  • 4 UILabels

这是我加载表格的代码:

- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];

[_activity startAnimating];
NSString *urlString = @"http://blablabla.com/getmydata";
NSLog(@"Loading URL=%@", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                                                       cachePolicy:BACKEND_CACHE_POLICY
                                                   timeoutInterval:BACKEND_TIMEOUT_ASINC];

[NSURLConnection sendAsynchronousRequest:request
                                   queue:[NSOperationQueue mainQueue]
                       completionHandler:^(NSURLResponse *response, NSData *receivedData, NSError *error) {
                           NSLog(@"Received data: %@", [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding]);
                           NSError *jsonParsingError = nil;
                           NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:receivedData
                                                                                    options:0
                                                                                      error:&jsonParsingError];

                           dispatch_async(dispatch_get_main_queue(), ^{
                               [_activity stopAnimating];
                               NSLog(@"Stop animating");
                               myData = [receivedData objectForKey:@"data"];
                               [mytable reloadData];
                           });

                       }];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"Just before dequeueReusableCellWithIdentifier for row %d", indexPath.row);
CustomViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
NSLog(@"Just after dequeueReusableCellWithIdentifier for row %d", indexPath.row);
NSDictionary *data = [_items objectAtIndex:indexPath.row];
NSString *photo = [data objectForKey:@"photo_url"];
if([photo isKindOfClass:[NSNull class]])
    photo = @"";
NSString *title = [data objectForKey:@"title"];
if([title isKindOfClass:[NSNull class]])
    title = @"";
NSString *subtitle = [data objectForKey:@"subtitle"];
if([subtitle isKindOfClass:[NSNull class]])
    subtitle = @"";
NSString *author = [data objectForKey:@"author"];
if([author isKindOfClass:[NSNull class]])
    author = @"";
NSString *createdAt = [data objectForKey:@"created_at"];
if([createdAt isKindOfClass:[NSNull class]])
    createdAt = @"";
MyImageDownloader *downloadingImage = [_downloadingImages objectForKey:indexPath];
if (downloadingImage == nil)
{
    if ((photo != nil) && (![photo isEqualToString:@""]))
    {
        [self startImageDownload:photo forIndexPath:indexPath];
    }
    [cell.photoIV setImage:[UIImage imageNamed:@"noimage"]];
}
else
{
    [cell.photoIV setImage:downloadingImage.image];
}
[cell.titleLb setText:title];
[cell.subtitleLb setText:subtitle];
[cell.authorLb setText:author];
[cell.createdAtLb setText:createdAt];
return cell;
}

如果我运行它,这是输出:

2016-05-20 02:14:22.571 MyApp[7913:2037477] Loading URL=http://blablabla.com/getmydata
2016-05-20 02:14:23.233 MyApp[7913:2037477] Received data: data for 3 items.
2016-05-20 02:14:23.240 MyApp[7913:2037477] Stop animating
2016-05-20 02:14:23.242 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:28.784 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:28.791 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:28.818 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:28.821 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 2
2016-05-20 02:14:28.842 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 2
2016-05-20 02:14:28.858 MyApp[7913:2037477] Unable to simultaneously satisfy constraints.(some problems with the labels, but in the Interface Builder there are no warnings)
2016-05-20 02:14:28.873 MyApp[7913:2037477] Unable to simultaneously satisfy constraints.(some problems with the labels, but in the Interface Builder there are no warnings)
2016-05-20 02:14:28.887 MyApp[7913:2037477] Unable to simultaneously satisfy constraints.(some problems with the labels, but in the Interface Builder there are no warnings)
2016-05-20 02:14:29.645 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:29.649 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:29.650 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:29.651 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:29.652 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 2
2016-05-20 02:14:29.653 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 2
2016-05-20 02:14:29.656 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:29.657 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 0
2016-05-20 02:14:29.658 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:29.659 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 1
2016-05-20 02:14:29.661 MyApp[7913:2037477] Just before dequeueReusableCellWithIdentifier for row 2
2016-05-20 02:14:29.662 MyApp[7913:2037477] Just after dequeueReusableCellWithIdentifier for row 2

如你所见,第一个dequeueReusableCellWithIdentifier花了5秒!!

我试图在一个dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0))中封装NSURLConnection sendAsynchronousRequest,但结果是一样的。

为什么这花了这么长时间?我错过了什么?

编辑1:

  • 关于cellForRow,我刚刚编辑了上面的方法,所以它包含了完整的代码。

  • 关于JSON解析,我会考虑这些建议,但正如你们中的一些人所指出的那样,问题不在于这个过程,而在于第一次加载单元格。

  • 要加载远程图像异步,我正在使用自己创建的类,但正如我所见,它与SDWebImage非常相似。

  • 我试图在viewDidLoad中进行首次加载,但没有成功,表格仍然需要5秒才能加载。

  • 关于cellId,我在viewDidLoad中执行此操作:

    UINib * nib = [UINib nibWithNibName:@“CustomViewCell”bundle:nil];

    [mytable registerNib:nib forCellReuseIdentifier:@“Cell”];

    [mytable setRowHeight:UITableViewAutomaticDimension];

  • 主要的事情,灵感来自安德鲁·罗曼诺夫(Andrew Romanov)的回答“你能不能长时间地移出琴弦来展示它?”我已经决定从单元格XIB文件中完全删除4个UILabel和表加载超高速(两个预加载的图像和后端的异步图像),所以问题出在UILabels上。

  • 知道这一点我试图删除其中的一些,但结果是一样的,只有一个UILabel(无关紧要哪一个)表仍然需要5秒才能加载第一个单元格。 / p>

  • 我试图在4个UI标签上添加宽度和高度约束,以防万一这是计算单元格尺寸的问题,但表格仍需要5秒钟来加载第一个单元格。如果您认为该单元格的Autolayout可能存在问题,我会尝试发布约束。

所以,最后,问题在于单元格XIB中的UILabel,我已经实现了多次自定义单元格,布局更复杂,没有任何问题所以这次我确定我遗漏了一些东西。

编辑2:

根据Andrew Romanov的建议,我在dequeueReusableCellWithIdentifier中设置了一个断点,我跳过并暂停调试器。如此低级别的细节,我不太了解:

UIKit`-[UITableView dequeueReusableCellWithIdentifier:forIndexPath:]:
0x29ee6d84 <+0>:   push   {r4, r5, r6, r7, lr}
0x29ee6d86 <+2>:   add    r7, sp, #0xc
0x29ee6d88 <+4>:   push.w {r8, r10, r11}
0x29ee6d8c <+8>:   sub    sp, #0x10
0x29ee6d8e <+10>:  mov    r5, r0
0x29ee6d90 <+12>:  movw   r0, #0xbc52
0x29ee6d94 <+16>:  movt   r0, #0xbb8
0x29ee6d98 <+20>:  mov    r10, r1
0x29ee6d9a <+22>:  add    r0, pc
0x29ee6d9c <+24>:  mov    r4, r2
0x29ee6d9e <+26>:  mov    r8, r3
0x29ee6da0 <+28>:  ldr    r1, [r0]
0x29ee6da2 <+30>:  mov    r0, r5
0x29ee6da4 <+32>:  blx    0x2a4a2240                ; symbol stub for: CFDictionaryRemoveAllValues$shim
->  0x29ee6da8 <+36>:  mov    r6, r0
0x29ee6daa <+38>:  cbnz   r6, 0x29ee6e1c            ; <+152>
0x29ee6dac <+40>:  movw   r0, #0xb99c
0x29ee6db0 <+44>:  movt   r0, #0xbb8
0x29ee6db4 <+48>:  movw   r2, #0x1c36
0x29ee6db8 <+52>:  movt   r2, #0xbba

该指令“0x29ee6da8&lt; + 36&gt;:mov r6,r0”第一次需要5秒,并且其余的执行速度超高。

除此之外,什么仪器可以最好地调试这个问题?

固定!!

根据Andrew Romanov的建议,我复制并粘贴了堆栈跟踪,发现很长时间是由于加载了自定义字体(4个标签使用了Info.plist中正确定义的自定义字体)。 我所做的是为UILabels设置systemFont并在willDisplayCell方法中设置自定义字体。这样表的加载速度非常快。

对于offtopic我很抱歉,我怎么能将这个问题设置为已回答?我该回答并检查一下吗? 非常感谢Andrew!

2 个答案:

答案 0 :(得分:0)

您是在代码中还是在xib文件中实现Cell的UI?如果在xib中你可能没有在xib中配置cellId? 另外我看到只有第一个单元格被创建了很长时间,我认为在第一次初始化之后会缓存一些内容 如果在soryboard文件中配置单元格,请尝试将Cell的UI移动到Xib文件,并使用UITableView的method为该单元格注册一个nib:

- (void)registerNib:(UINib *)nib  forCellReuseIdentifier:(NSString *)identifier

此外,您可以在此屏幕之前的某个地方(例如,在加载数据时)从nib尝试initiate the cell,之后系统可以将数据保存为现金以避免从磁盘读取文件。
此外,您可以在使用method加载数据时将图像加载到后台缓存中:

+ (UIImage *)imageNamed:(NSString *)name
               inBundle:(NSBundle *)bundle
compatibleWithTraitCollection:(UITraitCollection *)traitCollection

+ (UIImage *)imageNamed:(NSString *)name

来自文档:

  

如果匹配的图像对象尚未在缓存中,则此方法   从磁盘或可用资产中查找并加载图像数据   目录,然后返回结果对象。

答案 1 :(得分:0)

问题是由细胞内的UILabels引起的。他们使用自定义字体,我在单元格XIB文件中设置,所以第一次表格加载单元格时,它会加载自定义字体,这需要花费很多时间。

我为解决这个问题所采取的措施是:

  1. 在XIB文件中,将UILabels的字体设置为系统字体,因此第一次加载单元格时,自定义字体没有加载时间。

  2. 然后,在UITableView Delegate方法中设置UILabels自定义字体:

    • (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
  3. 这样加载第一个单元格的时间非常短。