NSUserDefaults にいくつかのデータを保存する演習を行っています。データは、各画像に関する一意の識別情報を含むディクショナリ オブジェクトのリストです。リストは約 20 要素で、辞書内の情報は小さな文字列の集まりです。画像自体は確かに保存されません。
メモリ管理を学べるように、意図的に ARC を無効にしています。
プロファイラーを実行したところ、その配列を NSUserDefaults に格納する行でメモリ リークが発生しました。何か案は?
+ (void) addPhotoToRecentList:(NSDictionary *)photoInformation
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *photoId = [photoInformation objectForKey:(FLICKR_PHOTO_ID)];
NSMutableArray *recentPhotoList = [NSMutableArray arrayWithArray:[defaults objectForKey:RECENT_PHOTO_LIST]];
if (!recentPhotoList) {
recentPhotoList = [[[NSMutableArray alloc] init] autorelease];
} else {
NSLog(@"recentPhotoList is %@", recentPhotoList);
NSDictionary *photoElement;
for (photoElement in recentPhotoList) {
if ([photoId isEqualToString:[photoElement objectForKey:(FLICKR_PHOTO_ID)]]) {
[recentPhotoList removeObject:photoElement];
break;
}
}
if (recentPhotoList.count == MAX_PHOTOS) {
[recentPhotoList removeLastObject];
}
}
[recentPhotoList insertObject:photoInformation atIndex:0];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:RECENT_PHOTO_LIST];
[[NSUserDefaults standardUserDefaults] setObject:recentPhotoList forKey:RECENT_PHOTO_LIST];
[[NSUserDefaults standardUserDefaults] synchronize];
}
次の行を追加したことに注意してください。
[[NSUserDefaults standardUserDefaults] removeObjectForKey:RECENT_PHOTO_LIST];
それがメモリリークに役立つかどうか疑問に思っていますが、違いはありません。
その行をコメントアウトすると: [[NSUserDefaults standardUserDefaults] setObject:recentPhotoList forKey:RECENT_PHOTO_LIST];
その後、メモリリークはなくなりましたが、最近アクセスした画像のリストを保存することもできません.
これが、メモリリークがあるという事実を明らかにしたものです。Leaks Instrument を使用して Profiler を実行しました。また、その前にアナライザーを実行し、すべての問題を解決しました。警告はありません。
また、iOS のルールに違反している可能性もあります。頻繁に保存されるリストを変更できる 2 つの個別のビューがあります。近道として、それを NSUserDefaults に保存することにしたので、画像のリストを頻繁にクリックすると、毎回 NSUserDefaults に保存するように呼び出します。また、Recently Viewed List コントローラーを呼び出すと、NSUserDefaults からその情報が取得され、最新の写真が一番上に表示されるように更新されます。
これらのビューから到達可能なグローバル クラスを作成して一時的にリストに保存することを検討しています。その後、アプリがバックグラウンドになるか終了すると、NSUserDefaults を呼び出して保存しますが、このメモリ リークの原因を理解したいと思います。
この関数の呼び出し元は次のとおりです。
- (void) doActualWork
{
if (!self.selectedPhotoDict) {
NSLog(@"AMRO No picture selected must be an iPad");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return;
}
photoData = [[FlickrFetcher imageDataForPhotoWithFlickrInfo:self.selectedPhotoDict format:FlickrFetcherPhotoFormatLarge] retain];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[RecentTableViewController addPhotoToRecentList:self.selectedPhotoDict];
//With Splitviews this is how we refresh our view
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
[self loadPhoto];
}
}
これの呼び出し元は、フォト ビュー コントローラーをプッシュして特定の画像を読み込む別のビューからのものです。この関数をバックグラウンドで実行して、ネットワーク インジケーターをビジーに設定できるようにします。
- (void)setSelectedPhotoDict:givenSelectedPhotoDict
{
if (_selectedPhotoDict) {
[_selectedPhotoDict release];
}
_selectedPhotoDict = [givenSelectedPhotoDict retain];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
[self.imageView setNeedsDisplay]; // any time our Model changes, redraw our View
if (photoData) {
[photoData release];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self performSelector:@selector(doActualWork) withObject:Nil afterDelay:0.01];
}
}