SORU
10 Mart 2011, PERŞEMBE


Olmayan tembel iOS resim yükleme

Bir arka plan iş parçacığı Uİİmages yük ve iPad görüntülenmesini etmeye çalışıyorum. Ancak, imageViews' görüntü özelliği manzarası.hiç bir tepkime yok Ben yakında resim yükleme iOS tembel olduğunu anladım, ve bu soruya kısmi bir çözüm buldu

CGImage/UIImage lazily loading on UI thread causes stutter

Bu görüntünün iplik yüklü zorlar, ama görüntüyü görüntülerken hala bir kekeme var.

Örnek projemi burada bulabilirsiniz: http://www.jasamer.com/files/SwapTest.zip (edit: fixed version), SwapTestViewController kontrol edin. Resim kekeme görmek sürüklemeye çalışın.

Atlar bu (forceLoad yöntemi bir yığın taşması soru yukarıda yayınlanan alınır) test-kod oluşturdum:

NSArray* imagePaths = [NSArray arrayWithObjects:
                       [[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil], 
                       [[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil];

NSOperationQueue* queue = [[NSOperationQueue alloc] init];

[queue addOperationWithBlock: ^(void) {
    int imageIndex = 0;
    while (true) {
        UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]];
        imageIndex = (imageIndex 1)%2;
        [image forceLoad];

        //What's missing here?

        [self performSelectorOnMainThread: @selector(setImage:) withObject: image waitUntilDone: YES];
        [image release];
    }
}];

Bu Kekemelik önlenebilir biliyorum neden iki nedeni vardır:

(1) Apple Fotoğraflar uygulamasında Kekemelik olmadan görüntüleri yüklemek mümkün

(2) Bu kodu placeholder1 sonra kekelemeye neden olmaz ve placeholder2 bir kez kod yukarıda: bu Değiştirilmiş Sürümü görüntülenir

    UIImage* placeholder1 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil]];
[placeholder1 forceLoad];
UIImage* placeholder2 = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil]];
[placeholder2 forceLoad];

NSArray* imagePaths = [NSArray arrayWithObjects:
                       [[NSBundle mainBundle] pathForResource: @"a.png" ofType: nil], 
                       [[NSBundle mainBundle] pathForResource: @"b.png" ofType: nil], nil];
NSOperationQueue* queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock: ^(void) {
    int imageIndex = 0;
    while (true) {
        //The image is not actually used here - just to prove that the background thread isn't causing the stutter
        UIImage* image = [[UIImage alloc] initWithContentsOfFile: [imagePaths objectAtIndex: imageIndex]];
        imageIndex = (imageIndex 1)%2;
        [image forceLoad];

        if (self.imageView.image==placeholder1) {
            [self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder2 waitUntilDone: YES];
        } else {
            [self performSelectorOnMainThread: @selector(setImage:) withObject: placeholder1 waitUntilDone: YES];
        }                

        [image release];
    }
}];

Ancak, bellekteki tüm görüntüleri devam edemem.

Bu forceLoad tam iş - başka bir şey görüntüleri aslında görüntülenmeden önce var mı yok anlamına gelir. Aranızda bunun ne olduğunu, ve arka plan iş parçacığı içine koymak nasıl biliyor mu?

Teşekkürler, Julian

Güncelleme

Tommys birkaç ipucu bulabilirsiniz kullandı. Anladım ki çok fazla zaman alıyor Bu CGSConvertBGRA8888toRGBA8888, bu gecikme neden olan renk dönüşüm yok gibi görünüyor. İşte bu yöntem (ters) çağrı yığını.

Running        Symbol Name
6609.0ms        CGSConvertBGRA8888toRGBA8888
6609.0ms         ripl_Mark
6609.0ms          ripl_BltImage
6609.0ms           RIPLayerBltImage
6609.0ms            ripc_RenderImage
6609.0ms             ripc_DrawImage
6609.0ms              CGContextDelegateDrawImage
6609.0ms               CGContextDrawImage
6609.0ms                CA::Render::create_image_by_rendering(CGImage*, CGColorSpace*, bool)
6609.0ms                 CA::Render::create_image(CGImage*, CGColorSpace*, bool)
6609.0ms                  CA::Render::copy_image(CGImage*, CGColorSpace*, bool)
6609.0ms                   CA::Render::prepare_image(CGImage*, CGColorSpace*, bool)
6609.0ms                    CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms                     CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms                      CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms                       CALayerPrepareCommit_(CALayer*, CA::Transaction*)
6609.0ms                        CALayerPrepareCommit
6609.0ms                         CA::Context::commit_transaction(CA::Transaction*)
6609.0ms                          CA::Transaction::commit()
6609.0ms                           CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*)
6609.0ms                            __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
6609.0ms                             __CFRunLoopDoObservers
6609.0ms                              __CFRunLoopRun
6609.0ms                               CFRunLoopRunSpecific
6609.0ms                                CFRunLoopRunInMode
6609.0ms                                 GSEventRunModal
6609.0ms                                  GSEventRun
6609.0ms                                   -[UIApplication _run]
6609.0ms                                    UIApplicationMain
6609.0ms                                     main         

Son maske bit o önerilen değişiklikleri bir şey, ne yazık ki değişmedi.

CEVAP
10 Mart 2011, PERŞEMBE


UİKit ana iş parçacığı üzerinde sadece kullanılabilir. Kodunuzu bir iş parçacığı ana iş parçacığı dışındaki Uİİmage kullanın beri bu nedenle teknik olarak geçersiz. CoreGraphics yalnız (ve tembel olmayan decode) bir arka plan iş parçacığı üzerinde grafik yükü, ana konuya CGİmageRef post ve bir Uİİmage orada çevirmek için kullanın. (Kekeme olsa da istemezsin) mevcut uygulamada işe görünebilir, ama garanti değil. Batıl inanç ve kötü uygulama bu alan çevresinde savunulan bir sürü var gibi görünüyor, bazı kötü tavsiye buldunuz şaşırtıcı değil

Önerilen bir arka plan iş parçacığı üzerinde çalıştırmak için:

// get a data provider referencing the relevant file
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(filename);

// use the data provider to get a CGImage; release the data provider
CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, 
                                                    kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);

// make a bitmap context of a suitable size to draw to, forcing decode
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
unsigned char *imageBuffer = (unsigned char *)malloc(width*height*4);

CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();

CGContextRef imageContext =
    CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
                  kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);

CGColorSpaceRelease(colourSpace);

// draw the image to the context, release it
CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
CGImageRelease(image);

// now get an image ref from the context
CGImageRef outputImage = CGBitmapContextCreateImage(imageContext);

// post that off to the main thread, where you might do something like
// [UIImage imageWithCGImage:outputImage]
[self performSelectorOnMainThread:@selector(haveThisImage:) 
         withObject:[NSValue valueWithPointer:outputImage] waitUntilDone:YES];

// clean up
CGImageRelease(outputImage);
CGContextRelease(imageContext);
free(imageBuffer);

Eğer iOS 4 veya daha sonra eğer malloc/ücretsiz yapmaya gerek yok, sadece CGBitmapContextCreate ilgili parametre olarak NULL iletebilirsiniz, CoreGraphics ve kendi depolama çözelim.

Bu sensin çünkü post çözüm farklıdır:

  1. PNG veri kaynağından bir CGİmage oluşturur tembel yükleme uygulanır, bu mutlaka tam dolu ve sıkıştırılmış bir görüntü değil
  2. PNG olarak aynı boyutta bir bitmap bağlam oluşturur
  3. çizer C bir dizi bitmap bağlam — bu gerçek renk değerleri bir yere koymak zorunda beri tam yükleme ve açma zorlaması üzerine PNG veri kaynağından CGİmage bunlara erişim sağlayabiliriz. Bu adımla bağlantı forceLoad gidebildiği kadar.
  4. bitmap kapsamında bir görüntü dönüştürür
  5. ana iş parçacığı için resim mesajlar, muhtemelen bir Uİİmage olmak

Yani yok süreklilik nesne arasındaki şey dolu ve gösterilen şey; piksel verilerini geçer bir C dizi (yani, hiçbir fırsatı gizli maskaralık ve eğer koy içine dizi doğru sağlamak mümkün son görüntü.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • JamesAtiPhone

    JamesAtiPhon

    16 EYLÜL 2010
  • Joseph Hayhoe

    Joseph Hayho

    20 Mayıs 2010
  • pain975

    pain975

    27 NİSAN 2008