如何实现懒惰的json文件下载?

时间:2012-12-20 21:00:24

标签: ios json lazy-evaluation

我正在使用从我服务器上的数据库返回JSON格式化数据的Web服务,这些数据将显示在我的iPhone应用程序的某个视图中。

由于应用程序是免费的,所以我在我的数据库上获得了数千行,因此我必须下载JSON数据。

如何为我的JSON数据进行延迟加载?请非常感谢任何代码行。

感谢。

编辑1:

换句话说,我正在使用一个Sql请求,例如“select all from table”,然后将此数据作为JSON返回给我的应用程序。

我如何申请一些号码然后添加其他人等。?

1 个答案:

答案 0 :(得分:3)

术语“延迟加载”可能不完全适合单个JSON响应,它可能使我们倾向于采用一种方法而不是另一种方法。因此,让我解决我认为是潜在问题的问题,即使是非常庞大的服务器数据库,您仍希望改善用户体验:

  1. 如果JSON真的很大,你总是可以考虑一个“分页”比喻,在那里你下载第一个 x 记录,然后对下一个“页面”的后续“页面”进行后续请求。服务器数据。鉴于可能会在请求之间添加额外的服务器记录,您必须仔细考虑该实现,但这可能很容易。

  2. 使JSON更高效的另一种方法是限制初始请求中返回的数据。例如,第一个请求可以返回所有或合理的记录子集的标识符或标题信息,然后您可以随后请求其他详细信息(例如,用户进入详细信息屏幕)。

  3. 先前点的特定排列将是JSON当前包括任何大数据元素(例如,Base64编码的二进制数据,例如图像或声音文件)。通过从初始响应中获取这些,将解决许多问题。如果你可以返回这个二进制数据的URL,而不是数据本身,那肯定会使它自己延迟加载(同时让你能够轻松下载二进制信息而不是大33%的Base64编码) 。如果您正在处理图像,您可能还想考虑缩略图和大图像的概念,处理后者,特别是在延迟加载模型中。

  4. 您可以考虑实现支持流式传输的XML解析版本。标准NSXMLParser实现尝试在解析继续之前一次将整个XML提要(或尽可能多的?)加载到内存中(尽管方法名称暗示相反)。如果您的应用使用了LibXML2(例如在Apple的AdvancedURLConnections sample中),则可以在将初始数据呈现给用户的同时在后台继续下载和解析XML。这将同时产生大多数人与“延迟加载”相关联的好处(即,在向用户呈现结果之前不等待所有内容)和“急切加载”(即,它将下载下一个记录,因此它们是已经为用户准备好了。)

  5. 为了让我们提出更明智的建议,您真的需要分享有关JSON性质及其背后数据的更多信息,描述您认为“延迟加载”是解决方案的原因等等。您可能不想去在对数据进行一些分析之前,特定解决方案上的问题(例如,数千行的JSON仍然小于单个大图像)。


    <强>更新

    如果您要采用第一种方法,首先需要更改您的Web服务,以便响应请求时,它仅从特定记录号开始提供 n 记录。您可能还需要返回服务器上的记录总数,以便为用户提供一些可视化提示,以确定滚动的数量。

    其次,您需要更新iOS应用以支持此模型。因此,在您的UI中,当您滚动浏览结果时,您可能希望您的用户界面通过显示(a)实际结果(如果您已经获取)来响应滚动事件;或者(b)一些UI,它可视地指示正在检索有问题的记录,然后异步请求来自服务器的信息。如果您在UITableViewCell中执行此操作,则可能会执行以下操作:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        [self initiateRequestForFirstSetOfData];
    }
    
    - (void)initiateRequestForFirstSetOfData
    {
        // asynchronously retrieve the data from the server:
        //   (a) retrieve the total number of records
        //   (b) retrieve the actual data for the first n records
        // and when this is done, dispatch a `[self.tableView reloadData]` back to the
        // main queue.
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return self.totalNumberOfRecordsOnServer;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
        BOOL isAvailable = ... // do whatever logic to determine if this row has already been retrieved
    
        if (isAvailable)
        {
            // configure the cell like normal here
        }
        else
        {
            // configure the cell to indicate that a fetch is in progress here.
    
            // perhaps add a UIActivityIndicatorView and startAnimating it
    
            dispatch_async(backgroundQueue, ^{
    
                // initiate the request for the data (if you haven't already)
    
                dispatch_async(dispatch_get_main_queue(), ^{
    
                    // don't just update the UI here, but make sure the cell
                    // in question is still on screen by calling `UITableView`
                    // method `cellForRowAtIndexPath`, not to be confused with
                    // this method, which is a `UITableViewController` method.
    
                    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                    if (cell)
                    {
                        // update the cell: sometimes you can get away with
                        // updating the cell directly, sometimes you want to
                        // just call something like:
                        //
                        // [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    }
                });
            });
        }
    
        cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
    
        return cell;
    }
    

    我可能会对上述相当简单的代码提出一些改进,但希望它能为您提供基本的想法。您(a)计算出有多少总数据行; (b)您检索第一个 n 记录; (c)当用户滚动到一个记录时,如果你已经记录它,你会显示它,但如果没有,请提供视觉提示,你将异步获取它们的数据; (d)当数据进入时,如果适用,更新UI。

    就像我说的,我可能会对上面的代码进行一些改进,例如,我可能不会在视图控制器本身中嵌入异步检索,而是在我的模型中执行此操作并使用一些委托模式来更新UI,我可能会使用操作队列而不是调度队列,所以我可以取消我们不再需要的请求等,但是你得到了基本的想法。

    如果您使用的是UICollectionViewController,则类似于上面的代码。如果您使用的是滚动视图,则模式非常相似,但您会回复UIScrollViewDelegate方法scrollViewDidScroll而不是像上面那样编写代码,但也会释放已经滚出屏幕的UIKit个元素。