2013-04-13 37 views
8

Tôi đang chuyển từ quản lý bộ nhớ thủ công sang ARC và gặp sự cố. Hầu hết thời gian, tôi đang thực hiện tải dữ liệu một cách không đồng bộ bằng cách gọi performSelectorInBackground trong các lớp mô hình của tôi. Vấn đề là tôi cần phải ngừng thực thi mã mô hình bất kỳ khi mô hình nhận được nil (release). Trong không arc, mọi thứ đều đơn giản - ngay khi người dùng đóng cửa sổ, bộ điều khiển của nó bắt đầu giải quyết chính nó và deallocates mô hình [_myModel release] của nó, và vì vậy mô hình dừng thực thi mã của nó (tải dữ liệu) và được gọi là phương thức dealloc của nó .ARC: gửi nil đến một đối tượng không gọi cho dealloc của nó ngay lập tức

Điều này có vẻ khác trong ARC. Mô hình vẫn thực thi mã ngay cả sau khi nhận được thông báo nil từ bộ điều khiển. Phương thức dealloc của nó được gọi sau khi thực thi mã (chỉ tải dữ liệu). Đây là vấn đề vì việc thực thi mã nên dừng ASAP khi người dùng đóng cửa sổ (bộ điều khiển). Đó là một loại thiếu kiểm soát đối với mã - bộ điều khiển cho mô hình - "biến mất, tôi không cần công việc của bạn nữa" nhưng mô hình vẫn "đang làm việc để hoàn thành công việc của mình" :).

Hãy tưởng tượng một mô hình thực hiện một số xử lý dữ liệu rất nặng với thời lượng 10 giây. Mô hình bắt đầu xử lý khi người dùng mở cửa sổ (bộ điều khiển). Nhưng hình ảnh người dùng thay đổi ý định và đóng cửa sổ, ngay sau khi mở nó. Mô hình vẫn thực hiện xử lý lãng phí. Bất kỳ ý tưởng làm thế nào để giải quyết hoặc workaround đó? Tôi không thích một ý tưởng để có một đặc biệt BOOL "shouldDealloc" tài sản trong mô hình của tôi và thiết lập để YES trong phương pháp điều khiển dealloc, và sử dụng trong điều kiện lớp mô hình của tôi. Có giải pháp thanh lịch hơn không?

Tôi đã thực hiện một số dự án demo để hiển thị sự cố. Để thử nghiệm, chỉ cần tạo ứng dụng xem đơn và dán mã. Tạo để buttons- "Start tính toán""Dừng tính toán" trong tập tin ViewController.xib, và kết nối IBActions của họ với startCalculationPressedstopCalculationPressed:

ViewController.h

#import "MyModel.h" 

@interface ViewController : UIViewController <MyModelDelegate> 

- (IBAction)startCalculationPressed:(id)sender; 
- (IBAction)stopCalculationPressed:(id)sender; 

@end 

ViewController.m

@interface ViewController(){ 

    __strong MyModel *_myModel; 
} 
@end 

@implementation ViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 
} 

- (void)didReceiveMemoryWarning 
{ 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

- (void)didCalculated 
{ 
    NSLog(@"Did calculated..."); 
} 

- (IBAction)startCalculationPressed:(id)sender 
{ 
    NSLog(@"Starting to calculate..."); 

    _myModel = nil; 
    _myModel = [[MyModel alloc] init]; 
    _myModel.delegate = self; 

    [_myModel calculate]; 
} 

- (IBAction)stopCalculationPressed:(id)sender 
{ 
    NSLog(@"Stopping calculation..."); 
    _myModel.delegate = nil; 
    _myModel = nil; 
} 
@end 

Thêm lớp MyModel mới cho dự án:

MyModel.h

@protocol MyModelDelegate <NSObject> 

    - (void)didCalculated; 

@end 

@interface MyModel : NSObject 

    @property (nonatomic, weak) id<MyModelDelegate> delegate; 

    - (void)calculate; 

@end 

MyModel.m

@implementation MyModel 

- (void)dealloc 
{ 
    NSLog(@"MyModel dealloc..."); 
} 

- (void)calculate 
{ 
    [self performSelectorInBackground:@selector(performCalculateAsync) withObject:nil]; 
} 

- (void)performCalculateAsync 
{ 
    // Performing some longer running task 
    int i; 
    int limit = 1000000; 
    NSMutableArray *myList = [[NSMutableArray alloc] initWithCapacity:limit]; 

    for (i = 0; i < limit; i++) { 

    [myList addObject:[NSString stringWithFormat:@"Object%d", i]]; 
    } 

    [self performSelectorOnMainThread:@selector(calculateCallback) withObject:nil waitUntilDone:NO]; 

} 

- (void)calculateCallback 
{ 
    [self.delegate didCalculated]; 
} 

@end 

CẬP NHẬT Martin là đúng, performSelectorOnMainThread luôn giữ tự, vì vậy không có w ay làm thế nào để dừng thực hiện mã trên thread khác (cả trong ARC và không phải ARC) vì vậy dealloc không được gọi ngay lập tức khi phát hành mô hình. Vì vậy, cần được thực hiện rõ ràng bằng cách sử dụng thuộc tính thích hợp (ví dụ như đại biểu) với kiểm tra có điều kiện.

+1

tại sao không chỉ giữ một bool được kiểm tra trong 'performCalculateAsync' cho vòng lặp và sẽ chỉ kết thúc hàm khi nó được đặt bởi' stopCalculationPressed' (sẽ cần một số loại probex prob) có thể chương trình thực tế của bạn không có vòng lặp hoặc một nơi nào đó bạn có thể đặt kiểm tra này một cách thích hợp nhưng nếu nó ... – Fonix

+0

@Fonix Bạn thấy nó chỉ là một mã trình diễn đơn giản. Có nhiều thứ hơn trong các lớp mô hình thực (xử lý, gọi lại và vv). Có tài sản thay thế như vậy sẽ buộc tôi thêm kiểm tra có điều kiện trên tất cả các mã làm cho nó xấu xí ... – Centurion

+1

Bạn không nên sử dụng quản lý bộ nhớ để kiểm soát hành vi của chương trình. Quản lý bộ nhớ là để quản lý bộ nhớ và không phải bất cứ thứ gì khác. Nếu bạn cần một chuỗi hoặc một số thao tác khác để chấm dứt, bạn phải sắp xếp một tín hiệu rõ ràng cho hiệu ứng đó độc lập với quản lý bộ nhớ. Thiết kế trước của bạn đã bị hỏng. –

Trả lời

6

Một đối tượng được deallocated nếu số phát hành của nó đi xuống không, hoặc trong ngôn ngữ ARC, nếu tham chiếu mạnh cuối cùng cho đối tượng đó đã biến mất.

[self performSelectorInBackground:@selector(performCalculateAsync) withObject:nil]; 

bổ sung thêm một tài liệu tham khảo mạnh để self, điều này giải thích tại sao các đối tượng không được deallocated trước khi thread nền đã hoàn tất.

Không có cách nào (mà tôi biết) để tạo chuỗi nền dừng "tự động". Điều tương tự cũng đúng đối với các khối bắt đầu bằng dispatch_async() hoặc cho NSOperation. Sau khi bắt đầu, chủ đề/khối/hoạt động phải theo dõi một số thuộc tính tại các điểm nơi nó được lưu để dừng lại.

Trong ví dụ của bạn, bạn có thể theo dõi self.delegate. Nếu điều đó trở thành nil, không ai là quan tâm đến kết quả nữa, do đó, chủ đề nền có thể trở lại. Trong trường hợp đó, sẽ có ý nghĩa khi khai báo thuộc tính delegateatomic.

Lưu ý rằng self.delegate cũng được tự động thiết lập để nil nếu bộ điều khiển xem được deallocated (vì nó là một tài sản yếu) ngay cả khi stopCalculationPressed chưa được gọi.

+0

Tôi muốn biết làm thế nào ví dụ của OP làm việc với quản lý bộ nhớ thủ công. Trong cả quản lý bộ nhớ ARC và thủ công 'performSelectorInBackground' làm tăng số lượng giữ lại, do đó, trong cả hai trường hợp, có vẻ như' MyModel' sẽ chỉ được dealloced khi 'performCalculateAsync' kết thúc. – Barjavel

+0

@Barjavel: Vâng, đó là một điểm tốt. –

Các vấn đề liên quan