2010-09-21 21 views
6

से छवियों को लोड करते समय स्क्रॉल दृश्य और तालिका दृश्य प्रदर्शन, मैं नेटवर्क पर जाने के बजाय डिस्क-आधारित छवि कैश का उपयोग कर अपने छवि-गहन आईफोन ऐप के प्रदर्शन को बेहतर बनाने की कोशिश कर रहा हूं। मैंने SDImageCache (http://github.com/rs/SDWebImage/blob/master/SDImageCache.m) के बाद अपनी छवि कैश का मॉडल किया है, और यह काफी समान है लेकिन असीमित कैश इन/आउट ऑपरेशन के बिना।डिस्क

मेरे पास कुछ स्क्रॉल दृश्य और तालिका दृश्य हैं जो इन छवियों को असीमित रूप से लोड करते हैं। यदि छवि डिस्क पर है, तो यह छवि कैश से लोड की जाती है, अन्यथा नेटवर्क अनुरोध किया जाता है और बाद का परिणाम कैश में संग्रहीत होता है।

जिस समस्या में मैं चल रहा हूं वह यह है कि जब मैं स्क्रॉल दृश्य या तालिका दृश्यों के माध्यम से स्क्रॉल करता हूं, तो छवि को डिस्क से लोड होने के कारण एक ध्यान देने योग्य अंतराल होता है। विशेष रूप से, स्क्रॉल व्यू पर एक पृष्ठ से दूसरे पृष्ठ पर जाने की एनीमेशन संक्रमण के बीच में एक छोटा सा फ्रीज होता है।

मैं इसका समाधान करने में की कोशिश की है: डिस्क पहुँच अनुरोध करने के लिए (SDImageCache के रूप में एक ही तरीके से) एक NSOperationQueue और NSInvocationOperation वस्तुओं का उपयोग करना

  • , लेकिन यह कम से अंतराल के साथ मदद नहीं करता है सब।
  • स्क्रॉल व्यू कंट्रोलर कोड को ट्वीक करना ताकि स्क्रॉल दृश्य अब स्क्रॉल न होने पर यह केवल छवियों को लोड कर सके। इसका मतलब यह है कि स्क्रॉल दृश्य स्क्रॉलिंग बंद होने पर डिस्क पहुंच केवल तभी होती है, लेकिन अगर मैं तुरंत अगले पृष्ठ पर स्क्रॉल करने का प्रयास करता हूं तो मैं डिस्क से छवि लोड के रूप में अंतराल को देख सकता हूं।

क्या मेरी डिस्क एक्सेस बेहतर प्रदर्शन करने या UI पर कम प्रभाव डालने का कोई तरीका है?

ध्यान दें कि मैं पहले ही स्मृति में छवियों को कैशिंग कर रहा हूं। तो एक बार सब कुछ स्मृति में लोड हो जाने पर, यूआई अच्छा और उत्तरदायी है। लेकिन जब ऐप शुरू होता है, या यदि कम मेमोरी चेतावनियां भेजी जाती हैं, तो मुझे इनमें से कई यूआई लैग का अनुभव होगा क्योंकि छवियों को डिस्क से लोड किया जाता है।

प्रासंगिक कोड स्निपेट नीचे दिए गए हैं। मुझे नहीं लगता कि मैं कुछ भी फैंसी या पागल कर रहा हूँ। अंतराल एक आईफोन 3 जी पर ध्यान देने योग्य प्रतीत नहीं होता है, लेकिन यह दूसरे-जनरल आइपॉड टच पर काफी स्पष्ट है।

छवि कैशिंग कोड:

यहाँ मेरी छवि कैशिंग कोड का एक टुकड़ा प्रासंगिक है। बहुत सीधा।

- (BOOL)hasImageDataForURL:(NSString *)url { 
    return [[NSFileManager defaultManager] fileExistsAtPath:[self cacheFilePathForURL:url]]; 
} 

- (NSData *)imageDataForURL:(NSString *)url { 
    NSString *filePath = [self cacheFilePathForURL:url]; 

    // Set file last modification date to help enforce LRU caching policy 
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; 
    [attributes setObject:[NSDate date] forKey:NSFileModificationDate]; 
    [[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:filePath error:NULL]; 

    return [NSData dataWithContentsOfFile:filePath]; 
} 

- (void)storeImageData:(NSData *)data forURL:(NSString *)url { 
    [[NSFileManager defaultManager] createFileAtPath:[self cacheFilePathForURL:url] contents:data attributes:nil]; 
} 

स्क्रॉल दृश्य नियंत्रक कोड

यहाँ कोड है कि मैं अपने स्क्रॉल दृश्य नियंत्रकों में छवियों को प्रदर्शित करने के लिए उपयोग की एक प्रासंगिक टुकड़ा है।

- (void)scrollViewDidScroll:(UIScrollView *)theScrollView { 
    CGFloat pageWidth = theScrollView.frame.size.width; 
    NSUInteger index = floor((theScrollView.contentOffset.x - pageWidth/2)/pageWidth) + 1; 

    [self loadImageFor:[NSNumber numberWithInt:index]]; 
    [self loadImageFor:[NSNumber numberWithInt:index + 1]]; 
    [self loadImageFor:[NSNumber numberWithInt:index - 1]]; 
} 

- (void)loadImageFor:(NSNumber *)index { 
    if ([index intValue] < 0 || [index intValue] >= [self.photoData count]) { 
     return; 
    } 

    // ... initialize an image loader object that accesses the disk image cache or makes a network request 

    UIView *iew = [self.views objectForKey:index];  
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag]; 
    if (imageView.image == nil) { 
     NSDictionary *photo = [self.photoData objectAtIndex:[index intValue]]; 
     [loader loadImage:[photo objectForKey:@"url"]]; 
    } 
} 

छवि लोडर वस्तु सिर्फ एक हल्के उद्देश्य यह है कि डिस्क कैश की जाँच करता है और चाहे या नहीं डिस्क या नेटवर्क से एक छवि को लाने के लिए फैसला करता है।

- (void)imageLoadedFor:(NSNumber *)index image:(UIImage *)image { 
    // Cache image in memory 
    // ... 

    UIView *view = [self.views objectForKey:index]; 
    UIImageView *imageView = (UIImageView *) [view viewWithTag:kImageViewTag]; 
    imageView.contentMode = UIViewContentModeScaleAspectFill; 
    imageView.image = image; 
} 

अद्यतन

मैं एप्लिकेशन के साथ प्रयोग कर रहा था, और मैं छवि कैश अक्षम और पुनः बदला हमेशा बनाने के लिए: एक बार यह किया है, यह छवि प्रदर्शित करने के लिए स्क्रॉल दृश्य नियंत्रक पर एक प्रणाली को बुलाती है नेटवर्क अनुरोध ऐसा लगता है कि छवियों को लाने के लिए बस नेटवर्क अनुरोधों का उपयोग करना स्क्रॉल दृश्यों और तालिका दृश्यों के माध्यम से स्क्रॉल करते समय एक ही अंतराल का कारण बनता है! यही है, जब एक नेटवर्क अनुरोध समाप्त होता है और स्क्रॉल व्यू पेज या टेबल सेल में छवि दिखायी जाती है, तो यूआई थोड़ी देर लगती है और इसमें कुछ हिस्सों के अंतराल होते हैं क्योंकि मैं इसे खींचने की कोशिश करता हूं।

डिस्क कैश का उपयोग करते समय अंतराल अधिक ध्यान देने योग्य प्रतीत होता है, क्योंकि अंतराल पृष्ठ संक्रमण पर हमेशा होता है। संभवतः मैं लोड की गई छवि को उपयुक्त UIImageView में निर्दिष्ट करते समय कुछ गलत कर रहा हूं?

इसके अलावा - मैंने छोटी छवियों (50x50 थंबनेल) का उपयोग करने की कोशिश की है और अंतराल में सुधार हुआ है। तो ऐसा लगता है कि प्रदर्शन हिट या तो डिस्क से बड़ी छवि लोड करने या एक बड़ी छवि को UIImage ऑब्जेक्ट में लोड करने के कारण है। मुझे लगता है कि स्क्रॉल व्यू और टेबल व्यू में लोड होने वाली छवियों के आकार को कम करने के लिए एक सुधार होगा, जो कि मैं फिर भी करने की योजना बना रहा था। हालांकि, मैं समझ नहीं पा रहा हूं कि अन्य फोटो-गहन ऐप कैसे प्रदर्शित करने में सक्षम हैं, जो प्रदर्शन की समस्याओं के बिना डिस्क या नेटवर्क पर जाने के बिना स्क्रॉल करने योग्य दृश्यों में सुंदर उच्च-रेज फ़ोटो की तरह दिखता है।

+0

हमें बड़ी छवियों वाले आईपैड पर एक ही समस्या है। अगर आपको कोई समाधान मिल जाए तो मैं आपको बता दूंगा –

+0

प्रदर्शन स्क्रॉल करने के लिए मुख्य कुंजी में से एक मुख्य धागा को कभी भी अवरुद्ध नहीं करना है। मुख्य धागा यूआई थ्रेड है, जिसे स्क्रीन पर उच्च फ़्रेमेट अपडेट और आपके कंट्रोलर से संबंधित ईवेंट कॉलबैक प्रबंधित करना होता है। अपने NSUURLConnection कोड को देखे बिना मैं यह नहीं कह सकता कि आप इसे एक अलग थ्रेड में कर रहे हैं या नहीं, हालांकि, यह मेरा पहला झटका होगा। क्या आप अपना नेटवर्क हैंडलिंग कोड पोस्ट कर सकते हैं? – Yetanotherjosh

+0

टीसीआईमेज व्यू जिथब परियोजना की जांच करें। आलसी लोडिंग छवियों और डिस्क कैशिंग के लिए उनके पास एक सुंदर साफ कार्यान्वयन है https://github.com/totocaster/TCImageView/blob/master/TCImageView.m –

उत्तर

0

डिस्कव्यू छवि वास्तव में छविदृश्य पर छवि खींचते समय पढ़ी जाती है। भले ही हम डिस्क से छवि पढ़ने को कैश करते हैं, यह तब तक प्रभावित नहीं होता है क्योंकि यह फ़ाइल के संदर्भ में रहता है। इस उद्देश्य के लिए आपको बड़ी छवियों के टाइलिंग का उपयोग करना पड़ सकता है।

सादर, दीपा

1

आप LazyTableImages नमूना एप्लिकेशन की जांच करनी चाहिए।

1

यदि आपने इसे नेटवर्क गतिविधि तक सीमित कर दिया है तो मैं यह सुनिश्चित करने के लिए आपके अनुरोध को समाहित करने का प्रयास करूंगा कि यह मुख्य धागे से 100% दूर है। जबकि आप NSURLConnection को असीमित रूप से उपयोग कर सकते हैं और इसके प्रतिनिधि तरीकों का जवाब दे सकते हैं, मुझे पृष्ठभूमि ऑपरेशन में एक सिंक्रोनस अनुरोध को लपेटना आसान लगता है। यदि आपकी जरूरतें अधिक जटिल हैं तो आप NSOperation या ग्रैंड सेंट्रल प्रेषण का उपयोग कर सकते हैं। एक (अपेक्षाकृत) एक imageLoader कार्यान्वयन में सरल उदाहरण हो सकता है:

// imageLoader.m 

// assumes that that imageCache uses kvp to look for images 
- (UIImage *)imageForKey:(NSString *)key 
{ 
    // check if we already have the image in memory 
    UImage *image = [_images objectForKey:key]; 

    // if we don't have an image: 
    // 1) start a background task to load an image from a file or URL 
    // 2) return a default image to display while loading 
    if (!image) { 
     [self performSelectorInBackground:@selector(loadImageForKey) withObject:key]; 
     image = [self defaultImage]; 
    } 

    return image; 
} 

- (void)loadImageForKey:(NSString *)key 
{ 
    NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init]; 

    // attempt to load the image from the file cache 
    UIImage *image = [self imageFromFileForKey:key]; 

    // if no image, load the image from the URL 
    if (!image) { 
     image = [self imageFromURLForKey:key]; 
    } 

    // if no image, return default or imageNotFound image 
    if (!image) { 
     image = [self notFoundImage]; 
    } 

    if ([_delegate respondsTo:@selector(imageLoader:didLoadImage:ForKey:)]) { 
     [_delegate imageLoader:self didLoadImage:image forKey:key]; 
    } 

    [pool release]; 
} 

- (UIImage *)imageFromURLForKey:(NSString *)key 
{ 
    NSError *error = nil; 
    NSData *imageData = [NSData dataWithContentsOfURL:[self imageURLForKey:key] 
               options:0 
               error:&error]; 

    UIImage *image; 

    // handle error if necessary 
    if (error) { 
     image = [self errorImage]; 
    } 

    // create image from data 
    else { 
     image = [UIImage imageWithData:imageData]; 
    } 

    return image; 
} 
0

मैं इस समस्या को मिला है - आप की सीमा तक पहुंच गए हैं कितनी तेजी से यूआई स्क्रॉल करते हुए एक छवि लोड कर सकते हैं - तो मैं सिर्फ काम करेंगे समस्या के आसपास और उपयोगकर्ता अनुभव में सुधार।

  1. केवल छवियों को लोड जब स्क्रॉल एक नया 'पेज' (स्थिर आयत) और
  2. मामले में जहां उपयोगकर्ता तेजी से अनुप्रयोग से स्क्रॉल कर सकते हैं संभाल करने के लिए एक पारदर्शी स्क्रॉल दृश्य के पीछे एक गतिविधि सूचक रखो पर है लोड सामग्री
0

यह आम तौर पर छवि डिकोडिंग होता है जिसमें समय लगता है, और यह यूआई को स्थिर करने का कारण बनता है (क्योंकि यह मुख्य धागे पर हो रहा है)। हर बार जब आप बड़ी छवि पर [UIImage imageWithData:] पर कॉल करते हैं, तो आपको एक हिचकी दिखाई देगी। एक छोटी छवि को डीकोड करना बहुत तेज़ है।

दो विकल्प:

  1. आप पहले प्रत्येक छवि का थंबनेल संस्करण लोड कर सकते हैं, तो didFinishScrolling पर 'पैनापन'। थंबनेल को इतनी जल्दी डीकोड करना चाहिए कि आप किसी भी फ्रेम को न छोड़ें।
  2. आप प्रत्येक छवि का एक थंबनेल संस्करण लोड कर सकते हैं या पहले लोडिंग इंडिकेटर दिखा सकते हैं, फिर पूर्ण थ्रेड रिज़ॉल्यूशन छवि को किसी अन्य थ्रेड पर डीकोड कर सकते हैं, और इसे तैयार होने पर इसे उप-कर सकते हैं। (यह वह तकनीक है जो मूल फ़ोटो ऐप में नियोजित है)।

मैं दूसरा दृष्टिकोण पसंद करता हूं; आजकल जीडीसी के साथ यह काफी आसान है।

संबंधित मुद्दे