手動メモリ管理から ARC に移行していますが、問題があります。ほとんどの場合、モデル クラスで performSelectorInBackground を呼び出して、非同期でデータの読み込みを実行しています。問題は、モデルが nil (リリース) を受け取ったときにモデル コードの実行を停止する必要があることです。ユーザーがウィンドウを閉じるとすぐに、コントローラーが割り当て解除を開始し、モデル [_myModel release] の割り当てを解除するため、モデルはコードの実行 (データの読み込み) を停止し、dealloc メソッドが呼び出されます。 .
これは ARC では異なるようです。コントローラーから nil メッセージを受信した後でも、モデルはコードを実行します。その dealloc メソッドは、コードの実行 (データのロード) 後にのみ呼び出されます。ユーザーがウィンドウ (コントローラー) を閉じると、コードの実行ができるだけ早く停止する必要があるため、これは問題です。これは、コードに対するある種の制御の欠如です-コントローラーはモデルに指示します-「離れてください、もうあなたの仕事は必要ありません」が、モデルはまだ「その仕事を完了するために働いています」:)。
モデルが 10 秒間の非常に負荷の高いデータ処理を実行するとします。ユーザーがウィンドウ (コントローラー) を開くと、モデルはその処理を開始します。しかし、ウィンドウを開いた直後に、気が変わってウィンドウを閉じるユーザーをイメージしてください。モデルは依然として無駄な処理を実行します。それを解決する方法または回避策はありますか?モデルに特別な BOOL "shouldDealloc" プロパティを持ち、コントローラーの dealloc メソッドで YES に設定し、モデル クラスの条件で使用するというアイデアは好きではありません。よりエレガントなソリューションはありますか?
問題を示すためにいくつかのデモプロジェクトを作成しました。テストするには、シングル ビュー アプリケーションを作成し、コードを貼り付けます。ViewController.xib ファイルに「計算開始」および「計算停止」ボタンを作成し、それらの IBActions をstartCalculationPressedおよびstopCalculationPressedに接続します。
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
プロジェクトに新しい MyModel クラスを追加します。
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
UPDATE Martin の言うとおりです。 performSelectorOnMainThread は常に自己を保持するため、他のスレッド (ARC と非 ARC の両方) でコードの実行を停止する方法がないため、モデルを解放するときに dealloc がすぐに呼び出されません。そのため、条件付きチェックで適切なプロパティ (デリゲートなど) を使用して明示的に行う必要があります。