Xóa hình ảnh UIImageNamed: FUD


117

Chỉnh sửa tháng 2 năm 2014: Lưu ý rằng câu hỏi này có từ iOS 2.0! Các yêu cầu về hình ảnh và xử lý đã thay đổi rất nhiều kể từ đó. Retina làm cho hình ảnh lớn hơn và tải chúng phức tạp hơn một chút. Với sự hỗ trợ tích hợp cho iPad và hình ảnh võng mạc, bạn chắc chắn nên sử dụng ImageNamed trong mã của mình .

Tôi thấy nhiều imageNamedngười nói rằng hiệu suất là xấu nhưng số người nói rằng hiệu suất là tốt - đặc biệt là khi kết xuất UITableViews. Xem ví dụ câu hỏi SO này hoặc bài viết này trên iPhoneDeveloperTips.com

UIImage's imageNamedphương pháp sử dụng để rò rỉ nên nó được tốt nhất tránh nhưng đã được cố định trong các phiên gần đây. Tôi muốn hiểu rõ hơn về thuật toán bộ nhớ đệm để đưa ra quyết định hợp lý về nơi tôi có thể tin tưởng hệ thống lưu trữ hình ảnh của mình vào bộ nhớ cache và nơi tôi cần phải đi xa hơn và tự mình thực hiện. Hiểu biết cơ bản hiện tại của tôi là nó là một đơn giản NSMutableDictionarycủa UIImagestham chiếu bởi tên tập tin. Nó trở nên lớn hơn và khi hết bộ nhớ, nó sẽ nhỏ hơn rất nhiều.

Ví dụ, có ai biết chắc chắn rằng bộ nhớ cache hình ảnh phía sau imageNamedkhông phản hồi didReceiveMemoryWarning? Có vẻ như Apple sẽ không làm điều này.

Nếu bạn có bất kỳ thông tin chi tiết nào về thuật toán bộ nhớ đệm, vui lòng đăng nó ở đây.


2
tôi cũng vậy. Có vẻ như SO không quá nhiều thiên tài như tôi nghĩ.
Rog

Có vẻ như bạn đã dành thời gian điều tra việc này. Bạn đã thực hiện bất kỳ thử nghiệm nào cho thấy bất kỳ tác động tiêu cực nào của hình ảnh UIImageNamed với phiên bản cảm ứng ca cao mới nhất chưa? Tạo một UITableView và thêm nhiều (hàng nghìn) hàng và xem hiệu suất có giảm đi không khi bạn hiển thị hình ảnh khác nhau trong mỗi hàng. Có lẽ sau đó mọi người có thể nhận xét về phát hiện của bạn.
stefanB

Trên thực tế, tôi đã không thực hiện nhiều thử nghiệm như vậy. Tôi sử dụng imageNamed và mặc dù tôi vẫn chưa tối ưu hóa bộ nhớ cho ứng dụng này, nhưng tôi không có bất cứ thứ gì có thể trỏ trực tiếp vào imageNamed như một bộ nhớ. Tôi đã chỉ ra một số bài đăng trên blog phàn nàn về imagedNamed ở đây và, tôi đã hỏi một câu hỏi tương tự trên bảng apple có nhiều hành động hơn. Tôi sẽ đăng lại một bản tóm tắt và liên kết ở đây khi tôi có thời gian và cảm thấy nó đã hủy bỏ các câu trả lời mà nó sẽ nhận được.
Rog

Câu trả lời:


85

tldr: ImagedNamed vẫn ổn. Nó xử lý bộ nhớ tốt. Sử dụng nó và ngừng lo lắng.

Chỉnh sửa tháng 11 năm 2012 : Lưu ý rằng câu hỏi này có từ iOS 2.0! Các yêu cầu về hình ảnh và xử lý đã thay đổi rất nhiều kể từ đó. Retina làm cho hình ảnh lớn hơn và tải chúng phức tạp hơn một chút. Với sự hỗ trợ tích hợp cho iPad và hình ảnh võng mạc, bạn chắc chắn nên sử dụng ImageNamed trong mã của mình. Bây giờ, vì lợi ích của hậu thế:

Các chủ đề chị em trên diễn đàn của Apple Dev nhận một số lưu lượng tốt hơn. Cụ thể Rincewind đã thêm một số quyền hạn.

Có một số vấn đề trong iPhone OS 2.x trong đó imageNamed: cache sẽ không được xóa, ngay cả sau khi có cảnh báo bộ nhớ. Đồng thời + imageNamed: đã được sử dụng rất nhiều không phải cho bộ nhớ cache, mà là vì sự tiện lợi, điều này có lẽ đã phóng đại vấn đề hơn mức đáng lẽ ra.

trong khi cảnh báo rằng

Về mặt tốc độ, có một sự hiểu lầm chung về những gì đang diễn ra. Điều lớn nhất mà + imageNamed: làm là giải mã dữ liệu hình ảnh từ tệp nguồn, điều này hầu như luôn làm tăng kích thước dữ liệu lên đáng kể (ví dụ: tệp PNG có kích thước màn hình có thể tiêu tốn vài chục KB khi nén, nhưng tiêu thụ hơn nửa MB giải nén - chiều rộng * chiều cao * 4). By Ngược + imageWithContentsOfFile: sẽ giải nén hình ảnh đó mỗi khi cần dữ liệu hình ảnh. Như bạn có thể tưởng tượng, nếu bạn chỉ cần dữ liệu hình ảnh một lần, bạn sẽ không giành được gì ở đây, ngoại trừ việc có một phiên bản hình ảnh được lưu trong bộ nhớ cache và có thể lâu hơn bạn cần. Tuy nhiên, nếu bạn có một hình ảnh lớn mà bạn cần phải vẽ lại thường xuyên, thì có những lựa chọn thay thế, mặc dù cách mà tôi muốn đề xuất chủ yếu là tránh vẽ lại hình ảnh lớn đó :).

Đối với hành vi chung của bộ nhớ cache, nó thực hiện bộ nhớ cache dựa trên tên tệp (vì vậy hai trường hợp + imageNamed: có cùng tên sẽ dẫn đến các tham chiếu đến cùng một dữ liệu được lưu trong bộ nhớ cache) và bộ nhớ cache sẽ phát triển động khi bạn yêu cầu thêm hình ảnh qua + imageNamed:. Trên iPhone OS, lỗi 2.xa ngăn bộ nhớ cache bị thu hẹp khi nhận được cảnh báo bộ nhớ.

Sự hiểu biết của tôi là bộ nhớ cache + imageNamed: nên tôn trọng cảnh báo bộ nhớ trên iPhone OS 3.0. Hãy kiểm tra nó khi bạn có cơ hội và báo cáo lỗi nếu bạn thấy không đúng như vậy.

Vì vậy, bạn có nó. imageNamed: sẽ không đập vỡ cửa sổ của bạn hoặc giết con bạn. Nó khá đơn giản nhưng nó là một công cụ tối ưu hóa. Đáng buồn thay, nó được đặt tên rất tệ và không có thứ tương đương nào dễ sử dụng - do đó mọi người lạm dụng nó và cảm thấy khó chịu khi nó chỉ thực hiện công việc của mình

Tôi đã thêm một danh mục vào UIImage để khắc phục điều đó:

// header omitted
// Before you waste time editing this, please remember that a semi colon at the end of a method definition is valid and a matter of style.
+ (UIImage*)imageFromMainBundleFile:(NSString*)aFileName; {
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
    return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}

Rincewind cũng bao gồm một số mã ví dụ để tạo phiên bản được tối ưu hóa của riêng bạn. Tôi không thể thấy nó là giá trị để bảo trì nhưng ở đây là cho sự hoàn chỉnh.

CGImageRef originalImage = uiImage.CGImage;
CFDataRef imageData = CGDataProviderCopyData(
     CGImageGetDataProvider(originalImage));
CGDataProviderRef imageDataProvider = CGDataProviderCreateWithCFData(imageData);
CFRelease(imageData);
CGImageRef image = CGImageCreate(
     CGImageGetWidth(originalImage),
     CGImageGetHeight(originalImage),
     CGImageGetBitsPerComponent(originalImage),
     CGImageGetBitsPerPixel(originalImage),
     CGImageGetBytesPerRow(originalImage),
     CGImageGetColorSpace(originalImage),
     CGImageGetBitmapInfo(originalImage),
     imageDataProvider,
     CGImageGetDecode(originalImage),
     CGImageGetShouldInterpolate(originalImage),
     CGImageGetRenderingIntent(originalImage));
CGDataProviderRelease(imageDataProvider);
UIImage *decompressedImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);

Đánh đổi với mã này là hình ảnh được giải mã sử dụng nhiều bộ nhớ hơn nhưng hiển thị nhanh hơn.


Có vẻ như trên iOS11, [UIImage imageNamed:] không loại bỏ hình ảnh khỏi bộ nhớ cache nếu hình ảnh đến từ danh mục xcasset. Điều này dẫn đến treo máy do hết bộ nhớ.
Juraj Antas

5

Theo kinh nghiệm của tôi, bộ nhớ cache hình ảnh được tạo bởi imageNamed không phản hồi các cảnh báo về bộ nhớ. Tôi đã có hai ứng dụng nhỏ gọn nhất có thể để quản lý bản ghi nhớ, nhưng vẫn không thể giải thích được vì thiếu bản ghi nhớ. Khi tôi ngừng sử dụng imageNamed để tải hình ảnh, cả hai ứng dụng đều trở nên ổn định hơn đáng kể.

Tôi sẽ thừa nhận rằng cả hai ứng dụng đều tải hình ảnh hơi lớn, nhưng không có gì là hoàn toàn khác thường. Trong ứng dụng đầu tiên, tôi chỉ bỏ qua hoàn toàn bộ nhớ đệm vì không có khả năng người dùng sẽ quay lại cùng một hình ảnh hai lần. Trong phần thứ hai, tôi đã xây dựng một lớp bộ nhớ đệm thực sự đơn giản thực hiện những gì bạn đã đề cập - giữ UIImages trong một NSMutableDictionary và sau đó xóa nội dung của nó nếu tôi nhận được cảnh báo bộ nhớ. Nếu imageNamed: được lưu vào bộ nhớ cache như vậy, thì tôi không nên thấy bất kỳ sự nâng cấp hiệu suất nào. Tất cả điều này đang chạy trên 2.2 - Tôi không biết liệu có bất kỳ tác động 3.0 nào về điều này hay không.

Bạn có thể tìm thấy câu hỏi khác của tôi xung quanh vấn đề này từ ứng dụng đầu tiên của tôi tại đây: Câu hỏi StackOverflow về bộ nhớ cache của UIImage

Một lưu ý khác - InterfaceBuilder sử dụng imageNamed dưới các trang bìa. Một điều cần lưu ý nếu bạn gặp phải vấn đề này.


Tôi đã thấy hành vi giống hệt nhau và việc đọc từ tệp đã giải quyết trực tiếp nó. Ngoài ra, nó cũng xảy ra khi tôi cố gắng tải một hình ảnh rất lớn - 11.456 x 3.226 px, 15MB. Lý thuyết của tôi là hệ thống hết bộ nhớ một phần thông qua hoạt động của bộ đệm ImageNamed. Và không có xử lý nào được tích hợp cho trường hợp này.
Dogweather
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.