3

少し使用すると、アプリが遅くなり、で応答しなくなるようですUITableViewController。かなり集中的な方法がいくつかありますが、これらが実行されると、アプリの全体的なパフォーマンスに影響を与える理由はありません。

私はそれが何であるかを決定するために楽器を使用してきました、しかしそれは非常に決定的ではないことを証明しています。まず、1つの方法で問題を再現することはできません。これは、一般的な使用法から発生しているようです。CPUを使用する主なものは、cellForRowAtIndexPathスレッドの使用状況に応じたものです。私は実際にかなり多くの計算を行いますが、2つのことを説明していません。1)この問題が時間の経過とともに発生する理由。TableViewControllerアプリを最初に起動したときに上下に数回スクロールでき、すべてが非常にスムーズです。2)3つまたは4つのセルを除いてすべて削除しても、無反応が残ります。

もう1つの観察結果はmodal ViewController、アプリが最初に読み込まれたときのアニメーションは非常にスムーズですが、後でこの恐ろしいギザギザのアニメーションが発生することもあります。繰り返しになりますが、このモーダルの却下(managedObjectContext保存を含む)にはかなり複雑な計算がいくつかあり、UITableViewその下(いくつかを意味するcellForRowAtIndexPath)を明らかにするのに近いですが、これら2つのことだけでアニメーションを仮想的に実現する方法はありません0fps。これらすべてのことから、私はリソースが使い果たされて戻ってこないのだと信じるようになりました。残念ながら、iOS環境については、次の前提条件を確認するのに十分な知識がありませんが、次のようになります。

  • メモリー。簡単に言えば、これは本当にできませんか?私のアプリは大量の画像を含む非常にメモリを消費しますが、iOSはRAMが不足し始めるとRAMから物を削除すると確信しています。その上、診断では、アプリで大量のラグが発生したときに50MBの空き容量があったことが示されています。

  • CPU。前回パフォーマンスの問題が発生したとき(メソッドの無限ループを呼び出したとき)のように、何かがCPUをすべて使い果たしている可能性があると思いましたが、アプリは必要な場合にのみCPUを使用しているようです。つまり、アプリがアイドル状態のときは、使用量が0に戻ります。これがいかに重要であるかを説明するためだけに。リソースとしてのCPUが何かに使い果たされていた場合、それはなぜcellForRowAtIndexPath時間の経過とともに苦労していたのかを説明します。ただし、食べ尽くされていないため、アプリを長時間使用cellForRowAtIndexPathするとCPUの使用量が多すぎる理由はありません。したがって、CPUが占有されていることが原因で問題が発生していることがわかりません。

当然のことながら、私が考えることができるのはこれらの2つのリソースだけなので、私は完全にそして完全に困惑しています。なぜアプリが時間の経過とともに遅くなるのかわかりません。実際、アプリが最初に起動したときに正常に動作するため、コードがどのように機能するかがわかりません。また、十分に残っているように見えるときに、どのようにリソースになるかがわかりません。

私はObjective-C/iOSの開発に慣れていないので、何か足りないものがあるのではないかと思います。あなたが私がそれを特定するのを手伝ってくれるなら、私はそれを本当に感謝します。

更新:との私のコードを投稿cellForRowAtIndexPathするwillDisplayCell

- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
myEntityForConfigureCell = [self.fetchedResultsController objectAtIndexPath:indexPath];

searchResultForConfigureCell = [searchResults objectForKey:myEntityForConfigureCell.trackId];


// Example of how I fill in cell information. There are far more than shown.
nameLabelForConfigureCell = (UILabel *)[cell viewWithTag:1001];
nameLabelForConfigureCell.text = myEntityForConfigureCell.name;

genreLabelForConfigureCell = (UILabel *)[cell viewWithTag:1002];
genreLabelForConfigureCell.text = searchResultForConfigureCell.genre;

// Example of how I do the ImageViews using AFNetworking
imageForConfigureCell = (UIImageView *)[cell viewWithTag:1000];
[imageForConfigureCell setImageWithURL:[NSURL URLWithString: searchResultForConfigureCell.artworkURL60]];
imageForConfigureCell.layer.cornerRadius = 9.4;
imageForConfigureCell.layer.masksToBounds = YES;

// The RateView for showing a start rating
rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];
rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
[cell.contentView addSubview:rateViewForConfigureCell];

// Setting Sections for all Core Data entries only if BOOL isLoading is YES, but **has** finished downloading informaton, i.e. just before completion.

if ([searchResults count] == [fetchedResultsController.fetchedObjects count]) {

    if (isLoading) {
        NSLog(@"Is loading so Setting Sections");
        isLoading = NO;
        [self cycleThroughEntriesAndSetSection];
    }
    else if (!isLoading){
        [loadingHudView removeFromSuperview];
    }

}

}

そして今のためにwillDisplayCell

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
myEntityForDisplayCell = [self.fetchedResultsController objectAtIndexPath:indexPath];
searchResultForDisplayCell = [searchResults objectForKey:myEntityForDisplayCell.trackId];

UILabel *currentPrice = (UILabel *)[cell viewWithTag:1003];

if ([myEntityForDisplayCell.price1 floatValue] == [searchResultForDisplayCell.price2 floatValue]) {
   currentPrice.textColor = [UIColor blackColor];
}
else if ([myEntityForDisplayCell.price1 floatValue] > [searchResultForDisplayCell.price2 floatValue])
{
    currentPrice.textColor = [UIColor colorWithRed:0 green:0.9 blue:0 alpha:1];
}
else if ([myEntityForDisplayCell.price1 floatValue] < [searchResultForDisplayCell.price2 floatValue])
{
    currentPrice.textColor = [UIColor redColor];
}

cell.backgroundColor = nil;

if (isLoading) {
    loadingHudView.numOne = [searchResults count];
    loadingHudView.numTwo = [fetchedResultsController.fetchedObjects count];
    [loadingHudView setNeedsDisplay];
}

if ([searchResults count] == [fetchedResultsController.fetchedObjects count]) {

    [loadingHudView removeFromSuperview];

    if ([myEntityForDisplayCell.price1 floatValue] < [searchResultForDisplayCell.price2 floatValue]) {

        cell.backgroundColor = nil;
    }
    else
    {
        cell.backgroundColor = [UIColor colorWithRed:1 green:0.85 blue:0 alpha:0.45];
    }
}

}

更新2:アナライザーツールで何かを見つけました。それがどのようにリークであるかは完全にはわかりませんが、とにかくここに行きます。

SearchResult *searchResult = [[SearchResult alloc] init];

for (id i in fetchedResultsController.fetchedObjects) {
    MyEntity *myEntity = i;
    searchResult = [searchResults objectForKey:myEntity.id];

このコードは、新しいエンティティがCore Dataデータベースに追加されるたびに、つまり特に頻繁に実行されることはないでしょう。

4

2 に答える 2

4

3 つの観察:

  1. あなたは「多くの画像は、RAM が不足し始めると iOS が RAM から何かを削除すると確信しています」と言いますimageNamed。画像のキャッシュを行うときは、独自のNSCache. これは、大きな画像を使用している場合に特に重要です。

  2. インストゥルメントを使用してパフォーマンスのボトルネックがどこにあるかを特定する方法の実際の例については、WWDC 2012 - Building Concurrent User Interfaces on iOSを参照してください。

  3. 最後に、 Instrumentsを使用してリークを見つける方法のガイダンスについては、Instruments を使用してリークを見つけるを参照してください。また、静的アナライザーを見落とさないでください。これは、ARC を使用しない場合、または Core Foundation 呼び出しを使用する場合に重要です。

それ以外では、コードを見ずに問題の原因を特定することは困難です。


アップデート:

ソース コードを少し提供していただいたので、すぐに思い浮かんだのはaddSubviewofrateViewForConfigureCellです。セルが再利用されることを考えると、それをセルに繰り返し追加することになります。したがって、次のように置き換えることをお勧めします。

// The RateView for showing a start rating
rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];
rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
[cell.contentView addSubview:rateViewForConfigureCell];

と:

// The RateView for showing a start rating
rateViewForConfigureCell = (DYRateView *)[cell.contentView viewWithTag:1005]
if (!rateViewForConfigureCell) {
    rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
    rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
    rateViewForConfigureCell.tag = 1005;
    [cell.contentView addSubview:rateViewForConfigureCell];
}
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];

それを行うか、DYRateViewセル プロトタイプに を追加します。

于 2013-01-07T18:50:05.873 に答える
2

お察しのとおり、構成を実行するたびに新しいサブビューをセルに追加しています。コードのこのセクション:

// The RateView for showing a start rating
rateViewForConfigureCell = [[DYRateView alloc] initWithFrame:CGRectMake(73, 44, 75, 12) fullStar:[UIImage imageNamed:@"StarFullSmall.png"] emptyStar:[UIImage imageNamed:@"StarEmptySmall.png"]];
rateViewForConfigureCell.rate = [searchResultForConfigureCell.rating floatValue];
rateViewForConfigureCell.alignment = RateViewAlignmentLeft;
[cell.contentView addSubview:rateViewForConfigureCell];

毎回呼び出されるのは、新しいビューを割り当てて追加することです。十分なサイクルの後、セルにはこれらが数十個あり、完全に正常に見え、大量のメモリを消費しない可能性がありますが、合成エンジンはスクロール時にそれらすべてを考慮に入れる必要があるため、パフォーマンスが完全に低下します。テーブル上下。

このようなサブビューは一度追加して、configureCell メソッドで再構成する必要があります。nilセルのデキューから戻ったときに追加するかinit...、セルのサブクラスのメソッドに追加するか、セルの設計に使用している nib に追加します。

あなたはすでに他のサブビュー(これらすべてのviewWithTag:呼び出し)でこのようなことをしているように見えるので、なぜこのサブビューに当てはまらないのかわかりません。

これでもパフォーマンスの問題が解決されない可能性があることに注意してください-質問に示されているよりも「はるかに多く」ある場合、とにかく60fpsを取得できません-セルの前のセルに含めることができるサブビューの数は限られていますパフォーマンスが落ちるだけです。ここでは多くのことが行われているようです。あなたは初心者だとおっしゃっていますが、あまり野心的ではない導入プロジェクトが適切である可能性がありますか?

于 2013-01-07T20:14:58.683 に答える