2013-05-22 6 views
5

के साथ देखने में आता है मुझे बड़ी स्थानीय रूप से संग्रहीत छवि का आकार बदलने की आवश्यकता है (self.optionArray में निहित) और फिर इसे संग्रह दृश्य में दिखाएं। अगर मैं इसे दिखाता हूं, तो आईओएस छवियों का आकार बदलने की कोशिश कर रहा है क्योंकि मैं जल्दी से स्मृति-संबंधित दुर्घटनाओं को हल करता हूं।UICollectionView सेल छवि बदलती है क्योंकि यह जीसीडी

नीचे दिए गए कोड में, संग्रह दृश्य आसानी से स्क्रॉल करेगा, लेकिन कभी-कभी यदि मैं बेहद तेज़ी से स्क्रॉल करता हूं, तो एक गलत छवि होगी जो दिखाती है और फिर स्क्रॉलिंग को कम करने के बाद सही में बदल जाती है। cell.cellImage.image को nil को क्यों ठीक नहीं कर रहा है?

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 

    CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CustomTabBarCell" forIndexPath:indexPath]; 
    cell.cellImage.image = nil; 
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

      dispatch_async(queue, ^{ 
       cell.cellImage.image = nil; 
       UIImage *test = [self.optionArray objectAtIndex:indexPath.row]; 
       UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)]; 

       dispatch_sync(dispatch_get_main_queue(), ^{ 

        cell.cellImage.image = localImage2 
        cell.cellTextLabel.text = @""; 
        [cell setNeedsLayout]; 
       }); 

      }); 

     } 

    return cell; 
    } 

- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { 
    UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); 
    [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; 
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return newImage; 
} 

संपादित करें: मैं कैश करने के लिए पहली और नहीं के बराबर एक और async जोड़ा गया है और cell.image प्रारंभ। मुझे शुरुआती फास्ट स्क्रॉल पर एक ही समस्या है। हालांकि, स्क्रॉल बैक अप पर, अब यह निर्दोष है।

मैं जोड़ा इस:

-(void)createDictionary 
{ 
    for (UIImage *test in self.optionArray) { 
     UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)]; 
     [localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:[self.optionArray indexOfObject:test]]]; 
    } 
} 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    if (!localImageDict) { 
     localImageDict = [[NSMutableDictionary alloc]initWithCapacity:self.optionArray.count]; 
    } 
    else { 
     [localImageDict removeAllObjects]; 
    } 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

    dispatch_async(queue, ^{ 
     [self createDictionary]; 
    }); 

} 
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CustomTabBarCell" forIndexPath:indexPath]; 
    cell.cellImage.image = nil; 
    cell.cellImage.image = [[UIImage alloc]init]; 

     if ([localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]]) { 
      cell.cellImage.image = [localImageDict objectForKey:[NSNumber numberWithInt:indexPath.row]]; 
      cell.cellTextLabel.text = @""; 
     } 
    else { 

     cell.cellImage.image = nil; 
     cell.cellImage.image = [[UIImage alloc]init]; 
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

     dispatch_async(queue, ^{ 
      UIImage *test = [self.optionArray objectAtIndex:indexPath.row]; 
      UIImage *shownImage = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)]; 
      [localImageDict setObject:shownImage forKey:[NSNumber numberWithInt:indexPath.row]]; 

      dispatch_sync(dispatch_get_main_queue(), ^{ 

       cell.cellImage.image = shownImage; 

       cell.cellTextLabel.text = @""; 
       [cell setNeedsLayout]; 
      }); 

     }); 
    } 

} 
return cell; 

उत्तर

6

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

अपने ऐप के पदचिह्न को कम करने के लिए, आपको UIImage ऑब्जेक्ट्स की सरणी को बनाए रखना नहीं चाहिए। इसके बजाय, बस अपनी छवियों के लिए यूआरएल या पथों की एक सरणी बनाए रखें और फिर केवल यूआई द्वारा आवश्यक UIImage ऑब्जेक्ट्स बनाएं (एक प्रक्रिया जिसे आलसी लोडिंग कहा जाता है)। और एक बार जब छवि स्क्रीन छोड़ देती है, तो आप इसे जारी कर सकते हैं (UICollectionView, जैसे UITableView आपके लिए इस क्लीनअप का बहुत कुछ काम करता है जब तक आप छवियों के मजबूत संदर्भ बनाए रखते हैं)।

एक ऐप आमतौर पर केवल छवियों के लिए UIImage ऑब्जेक्ट्स को बनाए रखना चाहिए। प्रदर्शन कारणों से आप इन आकार की छवियों (उदाहरण के लिए NSCache का उपयोग कर) कैश कर सकते हैं, लेकिन जब आप स्मृति में कम चलाते हैं तो कैश स्वचालित रूप से शुद्ध हो जाएंगे।

अच्छी बात यह है कि आप स्पष्ट रूप से पहले से ही अतुल्यकालिक प्रक्रिया में अच्छी तरह से जानते हैं। वैसे भी, कार्यान्वयन तो ऐसा दिखाई देगा:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    CustomTabBarCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CustomTabBarCell" forIndexPath:indexPath]; 

    NSString *filename = [self.filenameArray objectAtIndex:indexPath.row]; // I always use indexPath.item, but if row works, that's great 

    UIImage *image = [self.thumbnailCache objectForKey:filename];   // you can key this on whatever you want, but the filename works 

    cell.cellImage.image = image;           // this will load cached image if found, or `nil` it if not found 

    if (image == nil)              // we only need to retrieve image if not found in our cache 
    { 
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 

     dispatch_async(queue, ^{ 
      UIImage *test = [UIImage imageWithContentsOfFile:filename]; // load the image here, now that we know we need it 
      if (!test) 
      { 
       NSLog(@"%s: unable to load image", __FUNCTION__); 
       return; 
      } 

      UIImage *localImage2 = [self imageWithImage:test scaledToSize:CGSizeMake(test.size.width/5, test.size.height/5)]; 
      if (!localImage2) 
      { 
       NSLog(@"%s: unable to convert image", __FUNCTION__); 
       return; 
      } 

      [self.thumbnailCache setObject:localImage2 forKey:filename]; // save the image to the cache 

      dispatch_async(dispatch_get_main_queue(), ^{     // async is fine; no need to keep this background operation alive, waiting for the main queue to respond 
       // see if the cell for this indexPath is still onscreen; probably is, but just in case 

       CustomTabBarCell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath]; 
       if (updateCell) 
       { 
        updateCell.cellImage.image = localImage2 
        updateCell.cellTextLabel.text = @""; 
        [updateCell setNeedsLayout]; 
       } 
      }); 

     }); 
    } 

    return cell; 
} 

यह मानता है कि आप thumbnailCache के एक वर्ग संपत्ति है कि आप viewDidLoad, या जहां में प्रारंभ करेंगे एक NSCache के लिए एक मजबूत संदर्भ है परिभाषित करते हैं। कैशिंग दोनों दुनिया के सर्वश्रेष्ठ, इष्टतम प्रदर्शन के लिए स्मृति में छवियों को लोड करने का एक तरीका है, लेकिन जब आप मेमोरी प्रेशर का अनुभव करते हैं तो इसे रिलीज़ किया जाएगा।

स्पष्ट रूप से, मैं स्पष्ट रूप से मान रहा हूं कि "ओह, छवियों के नामों की एक सरणी के साथ छवियों की अपनी सरणी को प्रतिस्थापित करें", और मुझे पता है कि आपको शायद अपने कोड के विभिन्न हिस्सों के समूह में जाना होगा काम, लेकिन निस्संदेह यह आपकी स्मृति खपत का स्रोत है। जाहिर है, आप हमेशा अन्य मेमोरी मुद्दों (चक्र और समान बनाए रखना) हो सकते हैं, लेकिन आपके द्वारा पोस्ट किए गए स्निपेट में ऐसा कुछ भी नहीं है।

+0

सभी छवियां स्थानीय हैं, सर्वर से कोई भी डाउनलोड नहीं किया जाता है। क्या इससे आपकी कोई भी सिफारिशें बदलती हैं? – Eric

+0

@ एरिक आह, माफ करना मैंने उसे नोटिस नहीं किया। उस स्थिति में, मेरा दूसरा बिंदु खुद को प्रकट करने की संभावना नहीं है (हालांकि सैद्धांतिक रूप से यह कर सकता है)। और चौथा बिंदु भी एक मुद्दा से कम है। लेकिन अंक एक और तीन निश्चित रूप से प्रासंगिक हैं और यदि आप कैश का उपयोग करते हैं तो चौथे बिंदु के बारे में आप थोड़ा प्रदर्शन सुधार देख सकते हैं। मैं आपको सबसे धीमे संभावित डिवाइस पर अपने कोड का परीक्षण करने के लिए प्रोत्साहित करता हूं, क्योंकि आपको सिम्युलेटर या किसी नए डिवाइस पर इनमें से कुछ समस्याएं नहीं दिखाई देगी। – Rob

+0

क्या आप मेरे संपादन को देख सकते हैं? – Eric

1

मुझे एक ही समस्या थी लेकिन इसके बारे में एक अलग तरीके से चला गया।

मुझे "पॉप-इन" का मुद्दा भी था, जो छवियों को लोड किया गया था, क्योंकि एसिंक लोड हो गए थे, जब तक कि आखिरकार सही दिखाई नहीं देता था।

ऐसा होने का एक कारण यह है कि प्रारंभिक रूप से हटाए गए सेल के लिए वर्तमान इंडेक्सपाथ उस छवि के सूचकांक से मेल नहीं खाता जो आप इसमें डाल रहे हैं।

असल में, यदि आप 0-19 से जल्दी स्क्रॉल करते हैं और जिस सेल को आप अपडेट करना चाहते हैं वह # 20 है और आप इसे छवि # 20 दिखाना चाहते हैं, लेकिन यह अभी भी छवियों को 3, 7, 14 को असीमित रूप से लोड कर रहा है।

इसे रोकने के लिए, मैंने जो किया वह दो सूचकांक ट्रैक था; # 1) सबसे हालिया इंडेक्सपैथ जो सेल की वास्तविक स्थिति को प्रतिबिंबित करता है और # 2) वास्तव में एसिंक लोड होने वाली छवि के अनुरूप सूचकांक (इस मामले में वास्तव में इंडेक्सपैथ होना चाहिए जो आप सेलफोर्टेमेटिंडेक्सपेथ में गुज़र रहे हैं, इसे बनाए रखा जाता है एसिंक प्रक्रिया कतार के माध्यम से काम करती है, इसलिए वास्तव में कुछ छवि लोडिंग के लिए "पुराना" डेटा होगा)।

सबसे हालिया इंडेक्सपैथ प्राप्त करने का एक तरीका एक साधारण विधि बनाना है जो सेल के वर्तमान स्थान के लिए सिर्फ एक एनएसआईएनटेगर देता है। इसे वर्तमान इंडेक्स के रूप में स्टोर करें।

तब मैंने एक जोड़े को रखा यदि चेक किया गया कि दोनों वास्तव में छवि में भरने से पहले बराबर थे।

तो यदि (currentIndex == imageIndex) तो छवि लोड करें।

यदि आप एनएसएलओजी (@ "वर्तमान ...% d ... छवि ...% d", currentIndex, imageIndex) डालते हैं, तो उन बयान से पहले यदि आप मेल नहीं खाते हैं तो आप स्पष्ट रूप से स्पष्ट रूप से देख सकते हैं और एसिंक कॉल से बाहर निकलना चाहिए।

उम्मीद है कि इससे मदद मिलती है।

1

मुझे यह पता चला कि chuey101 क्या कह रहा है, भ्रमित। मैंने एक रास्ता निकाला और फिर महसूस किया कि chuey101 का मतलब वही था।

यदि यह किसी की मदद करने जा रहा है, तो छवियां चमकती हैं और चल रहे विभिन्न धागे की वजह से बदल जाती हैं। इसलिए, जब आप छवि संचालन के लिए धागे को जन्म देते हैं, तो यह एक विशिष्ट सेल नंबर के लिए तैयार होने जा रहा है, सी 1 कहें। लेकिन, आखिरकार जब आप वास्तव में सेल में अपनी छवि लोड करते हैं, तो यह उस मौजूदा सेल के रूप में जा रहा है जिसे आप देख रहे हैं, जिसे आपने स्क्रॉल किया था - सी 2 कहें। इसलिए, जब आप सी 2 तक स्क्रॉल करते थे, तो सी 2 धागे होते थे जो आपके सेल स्क्रॉल किए जाने पर प्रत्येक सेल के सामने थे। जो मैं समझता हूं, उससे ये सभी धागे अपनी छवियों को वर्तमान सेल, सी 2 में लोड करने का प्रयास करने जा रहे हैं। तो, आपके पास छवियों की चमक है।

इससे बचने के लिए, आपको वास्तव में यह जांचना होगा कि आप उस छवि को लोड कर रहे हैं जिसे आप उस सेल में लोड करना चाहते हैं जिसका आप लोड करना चाहते हैं। तो, इसमें छवि लोड करने से पहले collectionviewcell indexpath.row प्राप्त करें (loading_image_into_cell)। इसके अलावा, मुख्य थ्रेड (image_num_to_load) में थ्रेड i.e. को दूर करने से पहले जिस सेल के लिए आपने अपने थ्रेड को फेंक दिया था उसे प्राप्त करें। अब, लोड करने से पहले, जांचें कि ये दो संख्या बराबर हैं।

समस्या हल हो गई :)

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