gpt4 book ai didi

objective-c - 使用 performSelectorInBackground 时收到内存警告

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:27:00 26 4
gpt4 key购买 nike

我有一个 UITableView,当项目被选中时,它加载一个 viewController,在它内部使用 performSelectorInBackground 在后台执行一些操作。

如果您慢慢点击 tableView 中的项目(基本上允许在后台执行的操作完成),一切都会正常进行。但是,当您快速选择项目时,该应用会快速返回一些内存警告,直到它崩溃,通常是在大约 7 或 8 次“点击”或选择之后。

知道为什么会这样吗?当我将代码从后台线程移至主线程时,一切正常。您只是无法快速选择 tableView,因为它正在等待操作完成。

代码片段:

//this is called from - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
-(void) showLeaseView:(NSMutableDictionary *)selLease
{
LeaseDetailController *leaseController = [[LeaseDetailController alloc] initWithNibName:@"LeaseDetail" bundle:nil];
leaseController.lease = selLease;

//[leaseController loadData];
[detailNavController pushViewController:leaseController animated:NO];
[leaseController release];
}

//this is in LeaseDetailController
- (void)viewDidLoad {
[self performSelectorInBackground:@selector(getOptions) withObject:nil];
[super viewDidLoad];
}

-(void) getOptions
{
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
NSArray *arrayOnDisk = [[NSArray alloc] initWithContentsOfFile:[appdel.settingsDir stringByAppendingPathComponent:@"optionData"]];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(LEASE_ID contains[cd] %@)", [lease leaseId]];
self.options = [NSMutableArray arrayWithArray:[arrayOnDisk filteredArrayUsingPredicate:predicate]];

[arrayOnDisk release];
[apool release];
}

最佳答案

每次您在后台执行 getOptions 选择器时,真正发生的是代表您创建一个新线程,并在那里完成工作。当用户连续多次点击您的表格单元格时,每次都会创建一个新线程来处理工作。如果 getOptions 完成的工作需要一些时间才能完成,您将有多个线程同时调用 getOptions。也就是说,系统不会取消之前在后台执行getOptions的请求。

如果您假设执行 getOptions 完成的工作需要 N 字节的内存,那么如果用户连续点击五个表格单元格并且 getOptions 没有立即完成,那么您会发现您的应用那时正在使用 5 * N 字节。相反,当您将应用更改为在主线程上调用 getOptions 时,它必须等待每次调用 getOptions 完成,然后才能再次调用 getOptions。因此,当您在主线程上工作时,您不会遇到使用 5 * N 字节的内存同时处理五个 getOptions 实例的情况。

这就是当您在后台执行此工作并且用户点击多个表格单元格时内存不足的原因:您正在执行该工作的多个实例,并且每个实例都需要自己的内存量,并且当它们全部加起来,它超出了系统的容量。

当用户选择一个表格单元格并导航到一个新的 View Controller 时,您似乎只是调用了一次 getOptions。由于用户一次只会查看其中一个 View Controller ,因此您实际上不需要在后台同时运行多个 getOptions 实例。相反,您希望在启动新实例之前取消先前运行的实例。您可以使用 NSOperationQueue 来执行此操作,如下所示:

- (NSOperationQueue *)operationQueue
{
static NSOperationQueue * queue = nil;
if (!queue) {
// Set up a singleton operation queue that only runs one operation at a time.
queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
}
return queue;
}

//this is called from - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
-(void) showLeaseView:(NSMutableDictionary *)selLease
{
LeaseDetailController *leaseController = [[LeaseDetailController alloc] initWithNibName:@"LeaseDetail" bundle:nil];
leaseController.lease = selLease;

// Cancel any pending operations. They'll be discarded from the queue if they haven't begun yet.
// The currently-running operation will have to finish before the next one can start.
NSOperationQueue * queue = [self operationQueue];
[queue cancelAllOperations];

// Note that you'll need to add a property called operationQueue of type NSOperationQueue * to your LeaseDetailController class.
leaseController.operationQueue = queue;

//[leaseController loadData];
[detailNavController pushViewController:leaseController animated:NO];
[leaseController release];
}

//this is in LeaseDetailController
- (void)viewDidLoad {
// Now we use the operation queue given to us in -showLeaseView:, above, to run our operation in the background.
// Using the block version of the API for simplicity.
[queue addOperationWithBlock:^{
[self getOptions];
}];
[super viewDidLoad];
}

-(void) getOptions
{
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
NSArray *arrayOnDisk = [[NSArray alloc] initWithContentsOfFile:[appdel.settingsDir stringByAppendingPathComponent:@"optionData"]];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(LEASE_ID contains[cd] %@)", [lease leaseId]];
NSMutableArray * resultsArray = [NSMutableArray arrayWithArray:[arrayOnDisk filteredArrayUsingPredicate:predicate]];

// Now that the work is done, pass the results back to ourselves, but do so on the main queue, which is equivalent to the main thread.
// This ensures that any UI work we may do in the setter for the options property is done on the right thread.
dispatch_async(dispatch_queue_get_main(), ^{
self.options = resultsArray;
});

[arrayOnDisk release];
[apool release];
}

关于objective-c - 使用 performSelectorInBackground 时收到内存警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5122635/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com