2011-02-25 74 views
2

我有一個UITableView,當選擇項目時,加載一個viewController,它內部使用performSelectorInBackground在後臺執行一些操作。使用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]; 
} 

回答

2

你在後臺執行getOptions選擇每次,什麼是真正發生的事情是被代表您創建一個新的線程,並且工作正在那裏進行。當用戶連續多次點擊表格單元格時,每次都會創建一個新線程來處理工作。如果getOptions完成的工作需要一些時間才能完成,那麼您將有多個線程同時調用getOptions。也就是說,系統不會取消先前在後臺執行getOptions的請求。

如果您認爲需要N個字節的內存來執行getOptions完成的工作,那麼如果用戶點擊了一行中的五個表格單元並且getOptions沒有立即完成,那麼您會發現您的應用程序在該點使用5 * N字節。相反,當您更改應用程序以在主線程上調用getOptions時,它必須等待每次調用getOptions才能完成,然後才能再次調用getOptions。因此,當您在主線程上完成工作時,您不會遇到使用5 * N字節內存同時執行5個getOptions實例的情況。

這就是爲什麼當你在後臺完成這項工作時用戶會耗盡內存,並且用戶點擊多個表格單元:您正在執行多個工作實例,並且每個實例都需要自己的內存量,當它們所有這些都得到加起來,它不僅僅是系統可以省掉的。

看起來您只是在用戶選擇表格單元格並導航到新視圖控制器時調用getOptions一次。由於用戶一次只能查看其中一個視圖控制器,因此您並不需要在後臺同時進行多個getOptions實例的同時進行。相反,您想要在開始新實例之前取消先前運行的實例。您的幫助

- (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]; 
} 
+0

非常感謝,我用了那麼久纔回來給你道歉,但是當我試圖將此代碼添加到項目中,我得到了:你可以做到這一點使用一個NSOperationQueue,像這樣跟隨錯誤。它不喜歡dispatch_async命令:架構i386的未定義符號: 「_dispatch_queue_get_main」,引用自: - [LeaseDetailController getOptions] – FilthySlider 2011-03-14 21:44:24