0

私のiPhoneアプリでは、スクロールビューにaUIScrollViewとn個のUIImagesとUIButtonsを動的に追加しています。ここでは、画像はさまざまな URL から読み込まれ、ボタンのタイトルは SQlite データベースから取得されます。すべて順調。しかし、スクロールビューをスクロールすると、レベル = 1 のメモリ警告が表示され、しばらくするとレベル = 2 になり、アプリがクラッシュします。ARCを使用しています。この問題を解決するにはどうすればよいですか?

コード

- (void)setUpViewLayout{
    int newContentSize = [appDelegate.itemArray count] * 125;

    menuItemIdArray = [[NSMutableArray alloc]init];
    mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
    mainView.contentSize = CGSizeMake(newContentSize, 220);
    mainView.tag = 100;
    mainView.delegate = self;
    mainView.userInteractionEnabled = YES;
    mainView.backgroundColor = [UIColor clearColor];

    int xPosition = 20;

    for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
        [self createImage:xPosition];
        [self createButton];
        xPosition = xPosition + 120;
    }
    [self.view addSubview:mainView];
}

- (void)createImage:(int)xPosition{
    DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];

    NSString *url = [NSString stringWithFormat:@"%@",itemObj.notAvialableIcon];
    imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
    imgView.userInteractionEnabled = YES;
    imgView.tag = tagVal;

    [imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:@"item01.png"]];

    [mainView addSubview:imgView];
}
- (void)createButton{
    DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
    button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(5, 90, 100, 26);
    button.tag = tagVal;
    button.userInteractionEnabled = YES;
    button.tag = tagVal;
    button.titleLabel.textColor = [UIColor blueColor];
    button.titleLabel.font = [UIFont systemFontOfSize:9.0];
    NSString *name = [NSString stringWithFormat:@"%@",itemObj.itemStatus];
    itmName = [NSString stringWithFormat:@"%@",itemObj.itemName];

    NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];          
    [button setTitle:date forState:UIControlStateNormal]; 
    button.userInteractionEnabled = NO;
    button setBackgroundImage:[UIImage imageNamed:@"not_available_bttn_bck_img"] forState:UIControlStateNormal];

    [imgView addSubview:button];
}
4

7 に答える 7

2

私はこれに対する新しい応答を開いています。これは、私たちが見逃した簡単な解決策です。

あなたが投稿したコードから、これは横にある単なるテーブルビューです。したがって、独自のタイル スクロール ビューを作成する必要はありません。

開始するためのコードを次に示します。テーブル ビューを設定するときは、90 度回転させ、行の高さを設定し、区切り線を削除します。

tableView.transform = CGAffineTransformMakeRotation(0.5 * M_PI);
tableView.rowHeight = 120.0;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

回転後に正しい位置になるように、テーブル ビューのフレームを設定する必要があります。基本的に、現在のスクロール ビューのフレーム、またはその横のフレームと同じです。

テーブル ビューのデータ ソース メソッドのいくつかを次に示します。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [appDelegate.itemArray count];
}

テーブル セルは、単一の画像ビューとその画像ビュー上のボタンだけを持つ、非常に単純なカスタム テーブル セルにすることができます。イメージ ビューを回転させて、イメージが横向きではなく正しく表示されるようにすることもできます。または、画像をロードする前に、フォト エディターまたは画像エディターですべての画像を回転させることもできます。

それだけです。テーブル ビューは、いつものように、セルのリサイクルとメモリ使用の最適化を処理します。

于 2012-06-16T10:15:26.830 に答える
2

Abhishek は、スーパービューに追加された後にすべてのサブビューを解放する必要があるという点で完全に正しいです。漏れの原因になります。具体的には、スクロール ビューが画面から外れて解放されると、そのサブビューは解放されません。割り当てられたときから、保持カウントは 1 のままです。

ただし、スクロール ビューがまだ画面上にある限り、リークはありません。スーパービューはすべてのサブビューを保持します (つまり、保持カウントを 1 増やします)。サブビューが割り当てられたが解放されていない場合、保持カウントは 2 です。割り当てられ、解放されていた場合、保持カウントは 1 です。スクロール ビューが存在する限り、そのサブビューは正しく保持されます。

スクロール ビューがまだ表示されているときにメモリ警告が表示される場合、問題はメモリ リークではなく、単にメモリの過剰使用である可能性があります。大きなスクロール ビューに画像を追加し続けると、確実にメモリ超過の問題が発生します。

大きなスクロール ビューを画像で埋め、メモリの過剰使用を避けるには、ScrollViewSuiteデモの 3 番目の例であるタイリングをご覧ください。画像とボタンは同じサイズで、タイルとして機能できるため、これでうまくいくはずです。

アイデアは、セルの代わりに画像タイルをリサイクルするスクロール ビューから一種のテーブル ビューを作成することです。スクロール ビューはサブクラス化され、一連の再利用可能なタイルがそのインスタンス変数の 1 つとして保持されます。実装の鍵は、layoutSubviews で、表示領域から移動したタイルをスーパービューから削除し、新しく表示されるコンテンツのタイルをリサイクルしてサブビューとして追加することです。このようにして、表示されているタイルのみがメモリに読み込まれます。また、テーブル ビューがセルをリサイクルするのと同じように、タイルをリサイクルします。

スクロール ビューのサイズからすると、並べて表示してリサイクルする以外に選択肢がない可能性があります。それにもかかわらず、それは良い選択肢です。

更新: Wubao Li は基本的に何をする必要があるかを要約しています。ScrollViewSuite デモは、その方法を示しています。

于 2012-06-15T11:38:45.030 に答える
2

コードのどの部分がリークを引き起こしているかを特定する必要があります。これにはいくつかの方法があります。

1 つの方法は、組み込みのアナライザーを xcode で使用することです。コードを分析し、(いくつかの) 潜在的なメモリ リークを検出します。

インストルメント ツールは、これらのリークを見つけるための優れたツールでもあります。割り当て/リーク コンポーネントを使用して開始します。スクロールビューに移動し、ビューをスクロールした後にサンプルを実行します。リークが表示されるはずです。これで、リークを追跡し、計測器にコード内の正しい場所を直接見つけさせることができます。

3 番目のオプションは、コードを調べて、自分で何が起こっているかを把握することです。メモリ管理を理解することは、iOS デバイスのプログラミングの重要な部分です。

スクロールビューで使用しているコードをここに投稿してみてください。

于 2012-06-15T10:29:11.260 に答える
1

ここで 3 つの提案があります。

  • バックグラウンド スレッドで画像を読み込んでみる
  • この応答を確認してくださいiOS 5 にはガベージ コレクションがありますか?
  • を使用してleakinstrumentアプリケーションがリークしている場所を見つけ、その部分を最適に管理します
于 2012-06-15T10:48:42.397 に答える
1

これはAppleのバグです。UIScrollView は、次のコードでもリークします。

UIScrollView *s = [[UIScrollView alloc] initWithFrame:self.view.bounds];
s.contentSize = CGSizeMake(320, 800);
[self.view addSubview:s];
[s release];
于 2012-07-24T07:38:09.230 に答える
1
//you had allocated the things but did not release it ... it was the reason of leak


- (void)setUpViewLayout{
int newContentSize = [appDelegate.itemArray count] * 125;

// menuItemIdArray = [[NSMutableArray alloc]init]; why you are allocating this array

UIScrollView *mainView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 100, 480, 220)];
mainView.contentSize = CGSizeMake(newContentSize, 220);
mainView.tag = 100;
mainView.delegate = self;
mainView.userInteractionEnabled = YES;
mainView.backgroundColor = [UIColor clearColor];

int xPosition = 20;

for (tagVal = 0; tagVal < [appDelegate.itemArray count]; tagVal++) {
    [self createImage:xPosition];
    [self createButton];
    xPosition = xPosition + 120;
}
[self.view addSubview:mainView];
[mainView relese];//release scroll view here
}

- (void)createImage:(int)xPosition{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];

NSString *url = [NSString stringWithFormat:@"%@",itemObj.notAvialableIcon];
imgView = [[UIImageView alloc]initWithFrame:CGRectMake(xPosition+8, 48, 110, 123)];
imgView.userInteractionEnabled = YES;
imgView.tag = tagVal;

   [imgView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage  imageNamed:@"item01.png"]];

   [mainView addSubview:imgView];
   [imgView release]; //release imageview here
}
 - (void)createButton{
DataBaseClass *itemObj = [appDelegate.itemArray objectAtIndex:tagVal];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(5, 90, 100, 26);
button.tag = tagVal;
button.userInteractionEnabled = YES;
button.tag = tagVal;
button.titleLabel.textColor = [UIColor blueColor];
button.titleLabel.font = [UIFont systemFontOfSize:9.0];
NSString *name = [NSString stringWithFormat:@"%@",itemObj.itemStatus];
itmName = [NSString stringWithFormat:@"%@",itemObj.itemName];

NSString *date = [self changeDateFormat:itemObj.itemReleaseDate];          
[button setTitle:date forState:UIControlStateNormal]; 
button.userInteractionEnabled = NO;
button setBackgroundImage:[UIImage imageNamed:@"not_available_bttn_bck_img"] forState:UIControlStateNormal];

[imgView addSubview:button];
}

役に立ちますか

于 2012-06-15T10:35:12.487 に答える