我正在使用从我服务器上的数据库返回JSON格式化数据的Web服务,这些数据将显示在我的iPhone应用程序的某个视图中。
由于应用程序是免费的,所以我在我的数据库上获得了数千行,因此我必须下载JSON数据。
如何为我的JSON数据进行延迟加载?请非常感谢任何代码行。
感谢。
编辑1:
换句话说,我正在使用一个Sql请求,例如“select all from table”,然后将此数据作为JSON返回给我的应用程序。
我如何申请一些号码然后添加其他人等。?
答案 0 :(得分:3)
术语“延迟加载”可能不完全适合单个JSON响应,它可能使我们倾向于采用一种方法而不是另一种方法。因此,让我解决我认为是潜在问题的问题,即使是非常庞大的服务器数据库,您仍希望改善用户体验:
如果JSON真的很大,你总是可以考虑一个“分页”比喻,在那里你下载第一个 x 记录,然后对下一个“页面”的后续“页面”进行后续请求。服务器数据。鉴于可能会在请求之间添加额外的服务器记录,您必须仔细考虑该实现,但这可能很容易。
使JSON更高效的另一种方法是限制初始请求中返回的数据。例如,第一个请求可以返回所有或合理的记录子集的标识符或标题信息,然后您可以随后请求其他详细信息(例如,用户进入详细信息屏幕)。
先前点的特定排列将是JSON当前包括任何大数据元素(例如,Base64编码的二进制数据,例如图像或声音文件)。通过从初始响应中获取这些,将解决许多问题。如果你可以返回这个二进制数据的URL,而不是数据本身,那肯定会使它自己延迟加载(同时让你能够轻松下载二进制信息而不是大33%的Base64编码) 。如果您正在处理图像,您可能还想考虑缩略图和大图像的概念,处理后者,特别是在延迟加载模型中。
您可以考虑实现支持流式传输的XML解析版本。标准NSXMLParser
实现尝试在解析继续之前一次将整个XML提要(或尽可能多的?)加载到内存中(尽管方法名称暗示相反)。如果您的应用使用了LibXML2
(例如在Apple的AdvancedURLConnections sample中),则可以在将初始数据呈现给用户的同时在后台继续下载和解析XML。这将同时产生大多数人与“延迟加载”相关联的好处(即,在向用户呈现结果之前不等待所有内容)和“急切加载”(即,它将下载下一个记录,因此它们是已经为用户准备好了。)
为了让我们提出更明智的建议,您真的需要分享有关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
个元素。