2009-09-15 16 views
5

Được rồi, tôi đang gặp khó khăn trong việc theo dõi sự rò rỉ bộ nhớ này. Khi chạy kịch bản này tôi không thấy bất kỳ rò rỉ bộ nhớ nào, nhưng đối tượng của tôi đang leo lên. Các công cụ trỏ tới CGBitmapContextCreateImage> create_bitmap_data_provider> malloc, điều này chiếm 60% đối tượng của tôi.iPhone - UIImage Leak, CGBitmapContextCreateImage Leak

Mã này được gọi nhiều lần bằng NSTimer.

//GET IMAGE FROM RESOURCE DIR 
    NSString * fileLocation = [[NSBundle mainBundle] pathForResource:imgMain ofType:@"jpg"]; 
    NSData * imageData = [NSData dataWithContentsOfFile:fileLocation]; 
    UIImage * blurMe = [UIImage imageWithData:imageData]; 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    UIImage * scaledImage = [blurMe _imageScaledToSize:CGSizeMake(blurMe.size.width/dblBlurLevel, blurMe.size.width/dblBlurLevel) interpolationQuality:3.0]; 
    UIImage * labelImage = [scaledImage _imageScaledToSize:blurMe.size interpolationQuality:3.0]; 
    UIImage * imageCopy = [[UIImage alloc] initWithCGImage:labelImage.CGImage]; 

    [pool drain]; // deallocates scaledImage and labelImage 

    imgView.image = imageCopy; 
    [imageCopy release]; 

Dưới đây là chức năng làm mờ. Tôi tin rằng vấn đề objectalloc nằm ở đây. Có lẽ tôi chỉ cần một đôi mắt mới. Sẽ là tuyệt vời nếu ai đó có thể hình dung ra điều này. Xin lỗi nó là loại dài ... Tôi sẽ cố gắng và rút ngắn nó.

@implementation UIImage(Blur) 
    - (UIImage *)blurredCopy:(int)pixelRadius 
    { 
     //VARS 
     unsigned char *srcData, *destData, *finalData; 
     CGContextRef context = NULL; 
     CGColorSpaceRef colorSpace; 
     void *   bitmapData; 
     int    bitmapByteCount; 
     int    bitmapBytesPerRow; 

     //IMAGE SIZE 
     size_t pixelsWide = CGImageGetWidth(self.CGImage); 
     size_t pixelsHigh = CGImageGetHeight(self.CGImage); 
     bitmapBytesPerRow = (pixelsWide * 4); 
     bitmapByteCount  = (bitmapBytesPerRow * pixelsHigh); 

     colorSpace = CGColorSpaceCreateDeviceRGB(); 
     if (colorSpace == NULL) { return NULL; } 

     bitmapData = malloc(bitmapByteCount); 
     if (bitmapData == NULL) { CGColorSpaceRelease(colorSpace); } 

     context = CGBitmapContextCreate (bitmapData, 
         pixelsWide, 
         pixelsHigh, 
         8,  
         bitmapBytesPerRow, 
         colorSpace, 
         kCGImageAlphaPremultipliedFirst); 
     if (context == NULL) { free (bitmapData); } 

     CGColorSpaceRelease(colorSpace); 
     free (bitmapData); 

     if (context == NULL) { return NULL; } 

     //PREPARE BLUR 
     size_t width = CGBitmapContextGetWidth(context); 
     size_t height = CGBitmapContextGetHeight(context); 
     size_t bpr = CGBitmapContextGetBytesPerRow(context); 
     size_t bpp = (CGBitmapContextGetBitsPerPixel(context)/8); 
     CGRect rect = {{0,0},{width,height}}; 

     CGContextDrawImage(context, rect, self.CGImage); 

     // Now we can get a pointer to the image data associated with the bitmap 
     // context. 
     srcData = (unsigned char *)CGBitmapContextGetData (context); 
     if (srcData != NULL) 
     { 

      size_t dataSize = bpr * height; 
      finalData = malloc(dataSize); 
      destData = malloc(dataSize); 
      memcpy(finalData, srcData, dataSize); 
      memcpy(destData, srcData, dataSize); 

      int sums[5]; 
      int i, x, y, k; 
      int gauss_sum=0; 
      int radius = pixelRadius * 2 + 1; 
      int *gauss_fact = malloc(radius * sizeof(int)); 

      for (i = 0; i < pixelRadius; i++) 
      { 
      .....blah blah blah... 
       THIS IS JUST LONG CODE THE CREATES INT FIGURES 
       ........blah blah blah...... 
       } 


      if (gauss_fact) { free(gauss_fact); } 
     } 

     size_t bitmapByteCount2 = bpr * height; 
     //CREATE DATA PROVIDER 
     CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, srcData, bitmapByteCount2, NULL); 

     //CREATE IMAGE 
     CGImageRef cgImage = CGImageCreate(
           width, 
           height, 
           CGBitmapContextGetBitsPerComponent(context), 
           CGBitmapContextGetBitsPerPixel(context), 
           CGBitmapContextGetBytesPerRow(context), 
           CGBitmapContextGetColorSpace(context), 
           CGBitmapContextGetBitmapInfo(context), 
           dataProvider, 
           NULL, 
           true, 
           kCGRenderingIntentDefault 
            ); 

     //RELEASE INFORMATION 
     CGDataProviderRelease(dataProvider); 
     CGContextRelease(context); 

     if (destData) { free(destData); } 
     if (finalData) { free(finalData); } 
     if (srcData) { free(srcData); } 


     UIImage *retUIImage = [UIImage imageWithCGImage:cgImage]; 
     CGImageRelease(cgImage); 

     return retUIImage; 
    } 

Điều duy nhất tôi có thể nghĩ rằng đang nắm giữ lên objectalloc được UIImage này * retUIImage = [UIImage imageWithCGImage: cgImage]; ... nhưng làm thế nào để làm tôi phát hành sau khi nó đã được trả lại? Hy vọng rằng ai đó có thể giúp đỡ.

+1

Tôi không biết liệu nó có liên quan đến rò rỉ hay không, nhưng bạn không nên gọi 'free (bitmapData)' trước khi bạn phát hành ngữ cảnh. Các khối bộ nhớ được cấp phát bằng 'malloc()' không có các bộ đếm lưu giữ, vì vậy nếu bạn gọi 'free()' nó được giải phóng ngay lập tức, ngay cả khi CGContext đang sử dụng nó. – benzado

+0

Bạn đang nhắm mục tiêu hệ điều hành iPhone 3.1 trở lên? Nó có thể là một lỗi trong khuôn khổ 3.0 hoặc trước đó. Nếu bạn đang sử dụng 3.1, bạn không cần phân bổ bitmapData cho 'CGBitmapContextCreate'. Ngoài ra '_imageScaledToSize' là cuộc gọi không có giấy tờ. Xin vui lòng không sử dụng nó. Một số người có thể không sẵn lòng trả lời câu hỏi của bạn nếu bạn đang sử dụng các cuộc gọi không có giấy tờ. – lucius

+0

hey bbullis21 mã mờ của bạn có hoạt động tốt trên thiết bị không ?? – Leena

Trả lời

0

Tôi đã nhìn thấy hành vi tương tự và đọc một số bài đăng tương tự khác. Tạo một nhóm autorelease dường như không giúp được gì. Có vẻ như thư viện hoặc là rò rỉ bộ nhớ hoặc lưu trữ nó trong một hồ bơi tự động cấp cao hơn để nó không được phát hành cho đến khi quá muộn. Tôi nghi ngờ có một lỗi trong khuôn khổ nhưng không thể chứng minh nó.

0

nó giúp nếu bạn thực sự quấn phân bổ hình ảnh và phát hành trong đối tượng hồ bơi chu kỳ tồn tại,

pool = [[NSAutoreleasePool alloc] init]; 

[imageView setImage: [UIImage imageNamed:"image1.png"]]; 
... 
[imageView setImage: [UIImage imageNamed:"image2.png"]]; 
... 
[imageView setImage: [UIImage imageNamed:"image3.png"]]; 
.... 
[pool drain]; 
[pool release]; 

Trong ví dụ image3.png này sẽ không được phát hành, nhưng image1 và image2 sẽ.

+0

Bạn không nên '-release' sau' -drain' (tương đương với '-release' trên các thời gian chạy không được thu thập rác) vì điều đó làm cho bản phát hành kép. – kennytm

1

Nhận clang và chạy dự án của bạn thông qua nó. Không chỉ nó sẽ tìm thấy rò rỉ (tĩnh) của bạn, nó sẽ giúp bạn hiểu thêm về các quy tắc đếm tham chiếu - ít nhất nó đã làm cho tôi.

+1

Hoặc chạy "Build & Analyze" trong Xcode 3.2 nếu bạn đang chạy Snow Leopard. Đó là clang và làm một công việc tốt đẹp của sơ đồ dòng chảy của thực hiện dẫn đến vấn đề. –

0

Một số ý tưởng/ý nghĩ:

Thứ nhất, đoạn mã này có rõ ràng vấn đề kể từ khi bạn có thể giải phóng BitmapData hai lần nếu bối cảnh thất bại trong việc phân bổ.

if (context == NULL) { free (bitmapData); } 

    CGColorSpaceRelease(colorSpace); 
    free (bitmapData); 

    if (context == NULL) { return NULL; } 

Tôi cũng đồng ý với benzado rằng bạn bitmapData cho đến khi bạn hoàn thành ngữ cảnh ... mặc dù nó không gặp sự cố gây nhầm lẫn. may mắn có lẽ.

Lưu ý rằng tôi khá chắc chắn rằng các nhà cung cấp dữ liệu và các bit là đề cập đến sẽ trở thành một phần của cgImage trở về từ CGImageCreate:

cung cấp Nguồn dữ liệu cho bitmap. Để biết thông tin về các định dạng dữ liệu được hỗ trợ, xem phần thảo luận bên dưới. Thạch anh giữ lại đối tượng này; khi trả lại, bạn có thể giải phóng nó một cách an toàn.

Vì vậy, điều đó có nghĩa là cho đến khi UIImage bạn trả lại được phát hành, tôi không nghĩ rằng nhà cung cấp dữ liệu cgImage hoặc bit sẽ biến mất. Vì vậy, có lẽ vấn đề là UIImage không phải là tất cả đi xa?

Tại sao bạn không sử dụng CGBitmapContextCreateImage() để tạo hình ảnh kết quả? (bạn có thể cần gọi CGContextFlush() để buộc vẽ bản đồ bitmap, nhưng có vẻ như bạn đang làm tất cả các pixel thay đổi theo cách thủ công, vì vậy có thể không cần thiết).

Dựa trên tài liệu mơ hồ, tôi không nghĩ bạn nên được tự do() 'ing con trỏ được trả về bởi CGBitmapContextGetData (nhưng đó là một chút đoán do sự mơ hồ của tài liệu).

thêm:

bạn không khởi srcData, destData & FinalData để NULL để thử nghiệm của bạn trước khi giải phóng() 'ing họ có vẻ nguy hiểm.

tất cả những gì đã nói (và làm thử một số trong những điều đó), chúng tôi đã có một sự rò rỉ trong Mac App của chúng tôi tại một thời điểm bởi vì trong 10.5.6 và trước đó một số khía cạnh của CIImage imageWithCGImage, imageByApplyingTransofrm, imageByCroppingToRect, NSBitmapImageRep initWithCIImage hoặc NSBitmapImageRep CGImage bị rò rỉ. Điều này đã được sửa trong 10.5.7 vì vậy có thể có điều gì đó tương tự tồn tại trong phiên bản hệ điều hành iPhone mà bạn đang thử nghiệm.

0

Thực ra nếu tôi nhớ [UIImage imageNamed] sẽ tạo bộ nhớ cache của riêng nó, nằm ngoài tầm kiểm soát của bạn. Điều đó sẽ giải thích cho việc sử dụng bộ nhớ mà bạn đang thấy. Bạn có thể đọc thêm tại đây - http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/.

4

Tôi đã sử dụng Quartz nhiều lần. Mỗi lần tôi làm là một cơn ác mộng. Theo như tôi thấy, và tôi đã điền một lỗi trên Apple gửi một dự án làm bằng chứng của vụ tai nạn, một trong 4 điều đúng:

  1. Quartz rò rỉ như là địa ngục
  2. Phải mất quá nhiều thời gian để giải phóng bộ nhớ
  3. sử dụng quá nhiều bộ nhớ không cần thiết hoặc
  4. kết hợp tất cả chúng.

Tôi đã từng tạo một dự án đơn giản để chứng minh điều đó. Dự án có một nút. Mỗi lần nhấn nút, một hình ảnh nhỏ (100x100 pixel) đã được thêm vào màn hình. Hình ảnh được tạo thành bởi hai lớp, chính hình ảnh và một lớp bổ sung chứa một đường đứt nét được vẽ xung quanh đường viền hình ảnh. Bản vẽ này được thực hiện bằng Quartz. Nhấn 8 lần nút khiến ứng dụng gặp sự cố. Bạn có thể nói: nhấn nút 8 lần và 16 hình ảnh đã được thêm vào màn hình, đây là lý do gây ra sự cố, phải không?

Thay vì vẽ đường viền bằng cách sử dụng Quartz, tôi quyết định vẽ đường viền trước trên PNG và chỉ thêm nó làm lớp cho đối tượng. Hai lớp giống nhau cho mỗi đối tượng được tạo. Đúng?

Tôi đã nhấp 100 lần, thêm 200 hình ảnh vào màn hình và không bị lỗi. Bộ nhớ không bao giờ vượt quá 800 Kb. Tôi có thể tiếp tục nhấp ... ứng dụng tiếp tục nhanh hơn và nhanh hơn. Không có cảnh báo bộ nhớ, không có sự cố.

Apple phải xem xét Quartz khẩn trương.

0

Tôi đã gặp vấn đề tương tự khi sử dụng CGBitmapContextCreate (..) với các sự cố xảy ra ngẫu nhiên và xây dựng.

tôi giải quyết điều này bằng cách tạo ra các UIImage từ dữ liệu bitmap sử dụng chức năng khác nhau:

Hãy thử:

CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); 

hoặc

provider = CGDataProviderCreateWithData((void *)pixelBuffer, sourceBaseAddr, sourceRowBytes * height, ReleaseCVPixelBufferFunction); 


CGImageRef imageRef = CGImageCreate(cvMat.cols,          // Width 
            cvMat.rows,          // Height 
            8,            // Bits per component 
            8 * cvMat.elemSize(),       // Bits per pixel 
            cvMat.step,          // Bytes per row 
            colorSpace,          // Colorspace 
            kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags 
            provider,          // CGDataProviderRef 
            NULL,           // Decode 
            false,           // Should interpolate 
            kCGRenderingIntentDefault);      // Intent 
0

Nếu bạn đang sử dụng thu gom rác thải, sử dụng CFMakeCollectable (posterFrame).Nếu bạn đang sử dụng quản lý bộ nhớ truyền thống, nó rất đơn giản:

return (CGImageRef)[(id)posterFrame autorelease]; 

Bạn cast CFTypeRef (trong trường hợp này, một CGImageRef) đến một con trỏ đối tượng Objective-C, hãy gửi thông điệp -autorelease, và sau đó đúc kết quả trở lại CGImageRef. Mẫu này hoạt động cho (hầu hết) bất kỳ loại nào tương thích với CFRetain() và CFRelease().

How to Autorelease a CGImageRef?