2009-07-08 12 views
16

私はUIScrollViewの内部に一連のイメージを並べてロードしています。私のアプリの例はhttp://www.42restaurants.comです。私の問題はメモリ使用量に伴い発生します。私は彼らが画面に表示されるように画像を読み込み、スクリーン上にない画像をアンロードしたいと思っています。コードでわかるように、ロードする部分を最小限に抑え、ローディング部分をNSOperationに割り当て、NSOperationQueueに配置する必要があります。すべては、不気味なスクロールの経験とは別に素晴らしい作品です。UIScrollViewでの最適化されたイメージのロード

これをさらに最適化し、各画像の読み込み時間を最小限に抑えるように、またはスクロールが不安定にならないようにするためのアイデアがあるかどうかはわかりません。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{ 
    [self manageThumbs];  
} 

- (void) manageThumbs{ 
    int centerIndex = [self centerThumbIndex]; 
    if(lastCenterIndex == centerIndex){ 
     return; 
    } 

    if(centerIndex >= totalThumbs){ 
     return; 
    } 

    NSRange unloadRange; 
    NSRange loadRange; 

    int totalChange = lastCenterIndex - centerIndex; 
    if(totalChange > 0){ //scrolling backwards 
     loadRange.length = fabsf(totalChange); 
     loadRange.location = centerIndex - 5; 
     unloadRange.length = fabsf(totalChange); 
     unloadRange.location = centerIndex + 6; 
    }else if(totalChange < 0){ //scrolling forwards 
     unloadRange.length = fabsf(totalChange); 
     unloadRange.location = centerIndex - 6; 
     loadRange.length = fabsf(totalChange); 
     loadRange.location = centerIndex + 5; 
    } 
    [self unloadImages:unloadRange]; 
    [self loadImages:loadRange]; 
    lastCenterIndex = centerIndex; 

    return; 
} 

- (void) unloadImages:(NSRange)range{ 
    UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0]; 
    for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){ 
     UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)]; 
     if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){ 
      ThumbnailView *thumbView = (ThumbnailView *)subview; 
      if(thumbView.loaded){ 
       UnloadImageOperation *unloadOperation = [[UnloadImageOperation alloc] initWithOperableImage:thumbView]; 
       [queue addOperation:unloadOperation]; 
       [unloadOperation release]; 
      } 
     } 
    } 
} 

- (void) loadImages:(NSRange)range{ 
    UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0]; 
    for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){ 
     UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)]; 
     if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){ 
      ThumbnailView *thumbView = (ThumbnailView *)subview; 
      if(!thumbView.loaded){ 
       LoadImageOperation *loadOperation = [[LoadImageOperation alloc] initWithOperableImage:thumbView]; 
       [queue addOperation:loadOperation]; 
       [loadOperation release]; 
      } 
     } 
    } 
} 

EDIT:本当に偉大な応答のための 感謝。ここに私のNSOperationコードとThumbnailViewコードです。私は週末にいくつかのことを試しましたが、スクロール中に操作キューを一時停止し、スクロールが終了したときに再開するだけでパフォーマンスを向上させることができました。ここで

は私のコードスニペットです:次に

//In the init method 
queue = [[NSOperationQueue alloc] init]; 
[queue setMaxConcurrentOperationCount:4]; 


//In the thumbnail view the loadImage and unloadImage methods 
- (void) loadImage{ 
    if(!loaded){ 
     NSString *filename = [NSString stringWithFormat:@"%03d-cover-front", recipe.identifier, recipe.identifier]; 
     NSString *directory = [NSString stringWithFormat:@"RestaurantContent/%03d", recipe.identifier];  

     NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:@"png" inDirectory:directory]; 
     UIImage *image = [UIImage imageWithContentsOfFile:path]; 

     imageView = [[ImageView alloc] initWithImage:image andFrame:CGRectMake(0.0f, 0.0f, 176.0f, 262.0f)]; 
     [self addSubview:imageView]; 
     [self sendSubviewToBack:imageView]; 
     [imageView release]; 
     loaded = YES;  
    } 
} 

- (void) unloadImage{ 
    if(loaded){ 
     [imageView removeFromSuperview]; 
     imageView = nil; 
     loaded = NO; 
    } 
} 

私のロードとアンロード操作:

- (id) initWithOperableImage:(id<OperableImage>) anOperableImage{ 

    self = [super init]; 
    if (self != nil) { 
     self.image = anOperableImage; 
    } 
    return self; 
} 

//This is the main method in the load image operation 
- (void)main { 
    [image loadImage]; 
} 


//This is the main method in the unload image operation 
- (void)main { 
    [image unloadImage]; 
} 

答えて

15

私は、「かすかな」スクロールで少し困惑しています。 NSOperationQueueは別のスレッドで操作を実行するので、最悪の場合には空のUIImageViewsが画面に表示されることがあります。

まず、NSOperationだけでメインスレッドに干渉しないように、プロセッサに大きな影響を与えるものを探しています。第二に、NSOperationの設定と実行を取り巻く詳細を探して、メインスレッドを中断してスクロールに影響を与える可能性のあるロックと同期の問題を引き起こしている可能性があります。

考慮すべきいくつかの項目:

  1. これはあなたを与えるだろう単に「ロードされた場合」をチェック以下のすべてをスキップ(あなたThumbnailViewの単一イメージで開始時にロードし、キューイングNSOperationを無効にしてみてくださいNSOperationコードがパフォーマンスに影響を与えているかどうかをすぐにアイデア。

  2. -scrollViewDidScroll:は、単一のスクロールアクションの途中で多く度も発生する可能性があることに注意してください。どのようにスクロール移動のために、どのようにあなたの-centerThumbIndexがあるに応じて、同じアクションを複数回キューに入れようとしている可能性があります。-initWithOperableImageまたは-loadedでこれを説明していれば、ここでコード化すると同期/ロックの問題が発生します(下記3を参照)。 ThumbnailViewインスタンスの「アトミック」プロパティを使用してNSOperationが開始されたかどうかを追跡する必要があります。そのプロパティが設定されている場合は、別の操作のキューイングを防止し、NSOperationプロセスの最後にそのプロパティ(ロードされた状態)のみを解除します。

  3. NSOperationQueueは独自のスレッドで動作するため、NSOperation内で実行されるコードがメインスレッドと同期またはロックされていないことを確認してください。これは、NSOperationQueueを使用することの利点をすべて排除します。

  4. 「アンロード」操作の優先度は、ユーザーの利便性を優先し、メモリの保護は第2であるため、「ロード」操作の優先度より低いことを確認してください。

  5. NSOperationQueueが遅れた場合、空のサムネイルが表示される前に、エラーの高いマージンが得られるように、1ページ以上、または2ページ分の十分なサムネイルを保持してください。

  6. ロード操作では、「プレスケールされた」サムネイルが読み込まれ、フルサイズの画像を読み込んでリスケールまたは処理しないことを確認してください。これは、スクロール・アクションの途中で余分なオーバーヘッドが多くなることになります。さらに進んで、アルファチャンネルなしでPNG16に変換してください。これにより、視覚画像に検出可能な変化がないことを望むなら、少なくとも4:1のサイズの縮小が可能になります。さらにサイズを小さくする(8:1縮小)PVRTC形式の画像を使用することも検討してください。これは "ディスク"から画像を読み取るのにかかる時間を大幅に短縮します。

これが意味をなさない場合はお詫び申し上げます。投稿したコードに問題はありませんし、NSOperationまたはThumbnailViewクラスの実装で問題が発生する可能性がさらに高くなります。そのコードを見直さずに、私は条件を効果的に記述していないかもしれません。

ロードとアンロードのためのNSOperationコードと、NSOperationインスタンスとのやりとりの仕方を理解するには、少なくともThumbnailViewを十分に投稿することをお勧めします。これはある程度助け

希望、

バーニー

3

一つの選択肢スクロールが停止したとき、あまり視覚的に満足が、唯一の負荷イメージにあります。スクロールが使用停止したとき

-scrollViewWillBeginDragging: 

再度有効にローディング画像:

-scrollViewDidEndDragging:willDecelerate: 

UIScrollViewDelegate方法

は、画像のロードを無効にするフラグを設定します。 willDecelerate:パラメータがNOの場合、移動が停止しています。

2

問題はここにある:

UIImage *image = [UIImage imageWithContentsOfFile:path]; 

ねじまたはないあなたは多分起こるディスクからファイルを(読み込むときと思われます関係なく、私は完全にはわからない)すべてがストールする。あなたは通常、他の状況ではこれを見ません。なぜなら、まったくそのような大きな領域が動いていないからです。

1

この問題を調査しながら、私が興味のある2つの以上のリソースが見つかりました:

は「PageControlを」iPhoneサンプルプロジェクトをチェックアウト:http://developer.apple.com/iphone/library/samplecode/PageControl/index.html

それ怠惰な負荷はUIScrollViewの中にコントローラを表示します。

  • と -

ココアタッチlibにチェックアウト:http://github.com/facebook/three20ウェブ/ディスクから怠惰なロード写真/サムネイルことを 'TTPhotoViewController' クラスを持っています。

+0

ありがとうございました。私は間違いなく一見するでしょう。 –

0

サブコンテンツビューのスクロールビューに対するshouldRasterize = YESを設定します。魅力のようにカスタム作成されたスクロールビューの不規則な振る舞いを取り除くのが見られます。 :)

また、Xcodeの計測器を使用していくつかのプロファイリングを行います。 Ray Wenderlichによってプロファイリングのために作成されたチュートリアルを見てください。

関連する問題