การลบอิมเมจ UIImage ชื่อ: FUD


117

แก้ไขกุมภาพันธ์ 2014: โปรดทราบว่าคำถามนี้เกิดจาก iOS 2.0! ข้อกำหนดด้านรูปภาพและการจัดการมีการเปลี่ยนแปลงมากมายตั้งแต่นั้นมา Retina ทำให้ภาพใหญ่ขึ้นและโหลดซับซ้อนขึ้นเล็กน้อย ด้วยในตัวในการสนับสนุนสำหรับ iPad และภาพจอประสาทตา, คุณควรจะใช้อย่างแน่นอน ImageNamed ในรหัสของคุณ

ฉันเห็นหลายคนบอกว่าimageNamedไม่ดี แต่มีคนจำนวนเท่า ๆ กันบอกว่าประสิทธิภาพดี - โดยเฉพาะอย่างยิ่งเมื่อแสดงผลUITableViews ดูตัวอย่างคำถาม SO นี้หรือบทความนี้บน iPhoneDeveloperTips.com

UIImage's imageNamedวิธีการที่ใช้ในการรั่วไหลจึงหลีกเลี่ยงที่ดีที่สุด แต่ได้รับการแก้ไขในรุ่นล่าสุด ฉันต้องการทำความเข้าใจอัลกอริธึมการแคชให้ดีขึ้นเพื่อทำการตัดสินใจอย่างมีเหตุผลว่าฉันสามารถเชื่อถือระบบในการแคชรูปภาพของฉันได้ที่ใดและฉันต้องไปที่ใดให้มากขึ้นและดำเนินการด้วยตัวเอง ความเข้าใจพื้นฐานของฉันปัจจุบันคือว่ามันเป็นที่เรียบง่ายNSMutableDictionaryของการUIImagesอ้างอิงโดยชื่อไฟล์ มันจะใหญ่ขึ้นและเมื่อหน่วยความจำหมดก็จะเล็กลงมาก

ยกตัวอย่างเช่นไม่มีใครรู้ว่าสำหรับแน่ใจว่าอยู่เบื้องหลังแคชภาพimageNamedไม่ตอบสนองต่อdidReceiveMemoryWarning? ดูเหมือนว่าไม่น่าเป็นไปได้ที่ Apple จะไม่ทำเช่นนี้

หากคุณมีข้อมูลเชิงลึกเกี่ยวกับอัลกอริทึมการแคชโปรดโพสต์ไว้ที่นี่


2
ฉันด้วย. ดูเหมือนว่า SO จะไม่เต็มไปด้วยอัจฉริยะอย่างที่ฉันคิด
Rog

ดูเหมือนว่าคุณใช้เวลาตรวจสอบเรื่องนี้อยู่พอสมควร คุณได้ทำการทดลองใด ๆ ที่จะแสดงผลกระทบเชิงลบของอิมเมจ UIImage ชื่อด้วยโกโก้ทัชเวอร์ชันล่าสุดหรือไม่ สร้าง UITableView และเพิ่มแถวจำนวนมาก (หลายพัน) และดูว่าประสิทธิภาพลดลงหรือไม่เมื่อคุณแสดงภาพที่แตกต่างกันในทุกแถว บางทีผู้คนอาจแสดงความคิดเห็นเกี่ยวกับสิ่งที่คุณค้นพบ
stefanB

อันที่จริงฉันไม่ได้ทำการทดลองมากขนาดนั้น ฉันใช้ imageNamed และแม้ว่าฉันจะยังไม่ได้ปรับแต่งหน่วยความจำให้เหมาะสมกับแอพนี้ แต่ฉันก็ไม่มีสิ่งใดที่ฉันสามารถชี้ไปที่ imageNamed ได้โดยตรง ฉันได้ชี้ให้เห็นการโพสต์บล็อกสองสามรายการที่บ่นเกี่ยวกับ imagedNamed ที่นี่และฉันได้ถามคำถามที่คล้ายกันในบอร์ดแอปเปิ้ลซึ่งมีการดำเนินการมากกว่า ฉันจะโพสต์สรุปใหม่และเชื่อมโยงที่นี่เมื่อฉันมีเวลาและรู้สึกว่ามันมีคำตอบที่จะได้รับ
Rog

คำตอบ:


85

tldr: ImagedNamed ใช้ได้ จัดการกับหน่วยความจำได้ดี ใช้แล้วหมดกังวล

แก้ไขพฤศจิกายน 2012 : โปรดทราบว่าคำถามนี้เกิดจาก iOS 2.0! ข้อกำหนดด้านรูปภาพและการจัดการมีการเปลี่ยนแปลงมากมายตั้งแต่นั้นมา Retina ทำให้ภาพใหญ่ขึ้นและโหลดซับซ้อนขึ้นเล็กน้อย ด้วยการสนับสนุนในตัวสำหรับ iPad และภาพเรตินาคุณควรใช้ ImageNamed ในรหัสของคุณอย่างแน่นอน ตอนนี้เพื่อประโยชน์ของลูกหลาน:

ด้ายน้องสาวในฟอรั่มแอปเปิ้ลเดฟได้รับการจราจรที่ดีบางอย่าง โดยเฉพาะRincewind ได้เพิ่มอำนาจบางอย่าง

มีปัญหาใน iPhone OS 2.x ที่ imageNamed: แคชจะไม่ถูกล้างแม้ว่าจะมีคำเตือนหน่วยความจำก็ตาม ในเวลาเดียวกัน + imageNamed: มีการใช้งานจำนวนมากไม่ใช่สำหรับแคช แต่เพื่อความสะดวกซึ่งอาจขยายปัญหาได้มากกว่าที่ควรจะเป็น

ในขณะที่เตือนว่า

ในหน้าความเร็วมีความเข้าใจผิดทั่วไปเกี่ยวกับสิ่งที่เกิดขึ้น สิ่งที่ใหญ่ที่สุดที่ + imageNamed: ทำคือถอดรหัสข้อมูลรูปภาพจากไฟล์ต้นฉบับซึ่งมักจะขยายขนาดข้อมูลอย่างมีนัยสำคัญ (ตัวอย่างเช่นไฟล์ PNG ขนาดหน้าจออาจใช้ KB ไม่กี่โหลเมื่อบีบอัด แต่กินมากกว่าครึ่ง MB แตก - กว้าง * สูง * 4) โดย contrast + imageWithContentsOfFile: จะขยายขนาดภาพนั้นทุกครั้งที่ต้องการข้อมูลภาพ อย่างที่คุณสามารถจินตนาการได้ว่าหากคุณต้องการข้อมูลรูปภาพเพียงครั้งเดียวคุณจะไม่มีอะไรที่นี่นอกจากจะมีรูปภาพที่แคชไว้ห้อยอยู่รอบ ๆ และน่าจะนานกว่าที่คุณต้องการ อย่างไรก็ตามหากคุณมีภาพขนาดใหญ่ที่ต้องวาดใหม่บ่อยๆก็มีทางเลือกอื่นแม้ว่าสิ่งที่ฉันจะแนะนำเป็นหลักคือหลีกเลี่ยงการวาดภาพขนาดใหญ่นั้นใหม่ :)

ในส่วนที่เกี่ยวกับลักษณะการทำงานทั่วไปของแคชนั้นแคชจะขึ้นอยู่กับชื่อไฟล์ (ดังนั้นสองอินสแตนซ์ของ + imageNamed: ที่มีชื่อเดียวกันควรส่งผลให้มีการอ้างอิงถึงข้อมูลแคชเดียวกัน) และแคชจะเติบโตแบบไดนามิกเมื่อคุณขอรูปภาพเพิ่มเติมผ่าน + imageNamed :. ข้อผิดพลาดบน iPhone OS 2.xa ป้องกันไม่ให้แคชหดเมื่อได้รับคำเตือนเกี่ยวกับหน่วยความจำ

และ

ความเข้าใจของฉันคือ + imageNamed: cache ควรเป็นไปตามคำเตือนของหน่วยความจำบน iPhone OS 3.0 ทดสอบเมื่อคุณมีโอกาสและรายงานข้อบกพร่องหากคุณพบว่าไม่เป็นเช่นนั้น

คุณมีแล้ว imageNamed: จะไม่ทุบหน้าต่างของคุณหรือสังหารลูก ๆ ของคุณ มันค่อนข้างง่าย แต่เป็นเครื่องมือเพิ่มประสิทธิภาพ น่าเศร้าที่ชื่อไม่ดีและไม่มีความเท่าเทียมกันที่ใช้งานง่าย - ด้วยเหตุนี้ผู้คนจึงใช้มันมากเกินไปและอารมณ์เสียเมื่อมันทำงานได้ดี

ฉันเพิ่มหมวดหมู่ลงใน UIImage เพื่อแก้ไข:

// 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 ยังรวมโค้ดตัวอย่างไว้เพื่อสร้างเวอร์ชันที่ปรับให้เหมาะสมของคุณเอง ฉันไม่เห็นว่ามันคุ้มค่ากับการดูแล แต่นี่คือความสมบูรณ์

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);

ข้อเสียของรหัสนี้คือภาพที่ถอดรหัสใช้หน่วยความจำมากกว่า แต่การแสดงผลเร็วกว่า


ดูเหมือนว่าใน iOS11 [UIImage imageNamed:] จะไม่นำรูปภาพออกจากแคชหากรูปภาพมาจากแค็ตตาล็อก xcasset ซึ่งทำให้เกิดปัญหาเนื่องจากหน่วยความจำไม่เพียงพอ
Juraj Antas

5

จากประสบการณ์ของฉันแคชรูปภาพที่สร้างโดย imageNamed ไม่ตอบสนองต่อคำเตือนของหน่วยความจำ ฉันมีแอปพลิเคชั่นสองตัวที่ใช้งานน้อยที่สุดเท่าที่จะทำได้พอ ๆ กับการจัดการ mem แต่ก็ยังคงขัดข้องอย่างอธิบายไม่ได้เนื่องจากไม่มี mem เมื่อฉันหยุดใช้ imageNamed เพื่อโหลดภาพแอปพลิเคชั่นทั้งสองก็มีความเสถียรมากขึ้นอย่างมาก

ฉันยอมรับว่าทั้งสองแอปพลิเคชั่นโหลดรูปภาพค่อนข้างใหญ่ แต่ไม่มีอะไรที่จะผิดปกติไปโดยสิ้นเชิง ในแอปพลิเคชันแรกฉันเพิ่งข้ามการแคชไปเลยเพราะไม่น่าเป็นไปได้ที่ผู้ใช้จะกลับมาที่ภาพเดิมซ้ำสองครั้ง ในวินาทีที่สองฉันสร้างคลาสแคชที่เรียบง่ายโดยทำตามที่คุณพูดถึง - เก็บ UIImages ไว้ใน NSMutableDictionary จากนั้นล้างเนื้อหาหากได้รับคำเตือนจากหน่วยความจำ หาก imageNamed: เป็นแคชเช่นนั้นฉันไม่ควรเห็นการอัปเกรดประสิทธิภาพใด ๆ ทั้งหมดนี้ทำงานบน 2.2 - ฉันไม่รู้ว่ามีผลกระทบ 3.0 ในเรื่องนี้หรือไม่

คุณสามารถค้นหาคำถามอื่น ๆ เกี่ยวกับปัญหานี้ได้จากแอปแรกของฉันที่นี่: คำถาม StackOverflow เกี่ยวกับการแคช UIImage

หมายเหตุอื่น ๆ - InterfaceBuilder ใช้ imageNamed ภายใต้ฝาครอบ สิ่งที่ควรทราบหากคุณพบปัญหานี้


ฉันเห็นพฤติกรรมเหมือนกันทุกประการและการอ่านจากไฟล์สามารถแก้ไขได้โดยตรง นอกจากนี้ยังเกิดขึ้นเมื่อฉันพยายามโหลดภาพขนาดใหญ่มาก - 11,456 x 3,226 px, 15MB ทฤษฎีของฉันคือระบบมีหน่วยความจำไม่เพียงพอในบางส่วนผ่านการดำเนินการแคช ImageNamed และไม่มีการจัดการในตัวสำหรับกรณีนี้
Dogweather
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.