ios - आईओएस ऐप पर छवियों को कैश करने का सबसे अच्छा तरीका?




image uitableview (5)

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

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

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

जल्द ही, मेरे पास छवियों के लिए यूआरएल के साथ एक NSDictionary जिसे मुझे अपने UITableView में दिखाना है। प्रत्येक सेल में एक शीर्षक और एक छवि होती है। मैंने सफलतापूर्वक ऐसा किया था, हालांकि स्क्रॉलिंग लगी हुई थी, क्योंकि ऐसा लगता था कि जब भी वे स्क्रीन में आए थे तब कोशिकाओं ने अपनी छवि डाउनलोड की थी। मैंने थोड़ी सी खोज की, और SDWebImage पर SDWebImage मिला। इसने स्क्रॉल-लैग को दूर कर दिया। मुझे पूरा यकीन नहीं है कि उसने क्या किया, लेकिन मुझे विश्वास था कि उसने कुछ कैशिंग किया था। परंतु! हर बार जब मैं पहली बार ऐप खोलता हूं, तो मुझे कोई छवि नहीं दिखाई देती है, और मुझे नीचे स्क्रॉल करना होगा, और उनके आने के लिए बैक अप लेना होगा। और यदि मैं ऐप से होम-बटन से बाहर निकलता हूं, और फिर खोलता हूं, तो ऐसा लगता है कि कैशिंग काम कर रही है, क्योंकि स्क्रीन पर छवियां दिखाई दे रही हैं, हालांकि, अगर मैं एक सेल को नीचे स्क्रॉल करता हूं, तो अगले सेल में कोई छवि नहीं है। जब तक मैं इसे पीछे स्क्रॉल नहीं करता और बैक अप नहीं लेता, या यदि मैं उस पर क्लिक करता हूं। क्या यह कैशिंग को काम करना चाहिए? या वेब से डाउनलोड की गई छवियों को कैश करने का सबसे अच्छा तरीका क्या है? छवियों को अद्यतन रूप से अद्यतन किया जा रहा है, इसलिए मैं उन्हें परियोजना में आयात करने के करीब था, लेकिन मुझे अपडेट अपलोड किए बिना छवियों को अपडेट करने की संभावना है ..

लॉन्च पर पूरे टेबलव्यू के लिए सभी छवियों को कैश (कैश में कुछ है) के रूप में लोड करना असंभव है? क्या मैं कभी-कभी छवियों के बिना कोशिकाओं को क्यों देखता हूं?

और हाँ, मुझे समझने में कठिनाई हो रही है कि कैश क्या है।

--EDIT--

मैंने इसे एक ही आकार (500x150) की छवियों के साथ करने की कोशिश की, और पहलू-त्रुटि चली गई, हालांकि जब मैं ऊपर या नीचे स्क्रॉल करता हूं, तो सभी कोशिकाओं पर छवियां होती हैं, लेकिन पहले वे गलत हैं। कुछ मिलीसेकंड के लिए सेल दृश्य में आने के बाद, सही छवि दिखाई देती है। यह आश्चर्यजनक रूप से परेशान है, लेकिन हो सकता है कि यह कैसे होना चाहिए? .. ऐसा प्रतीत होता है कि यह पहले कैश से गलत इंडेक्स चुनता है। अगर मैं धीमी गति से स्क्रॉल करता हूं, तो मैं छवियों को गलत छवि से सही में झपकी देख सकता हूं। अगर मैं तेज़ी से स्क्रॉल करता हूं, तो मेरा मानना ​​है कि गलत छवियां हर समय दिखाई देती हैं, लेकिन मैं तेजी से स्क्रॉलिंग के कारण नहीं बता सकता। जब तेज़ स्क्रॉलिंग धीमा हो जाती है और अंततः बंद हो जाती है, तो गलत छवियां अभी भी दिखाई देती हैं, लेकिन स्क्रॉलिंग बंद होने के तुरंत बाद, यह सही छवियों को अपडेट करती है। मेरे पास एक कस्टम UITableViewCell क्लास भी है, लेकिन मैंने कोई बड़ा बदलाव नहीं किया है .. मैं अभी तक अपने कोड से बहुत ज्यादा नहीं गया हूं, लेकिन मैं नहीं सोच सकता कि क्या गलत हो सकता है .. शायद मेरे पास कुछ है गलत आदेश .. मैंने जावा, सी #, पीएचपी आदि में प्रोग्राम किया है, लेकिन मुझे ऑब्जेक्टिव सी को समझने में कठिनाई हो रही है, सभी .h और .m ... मेरे पास भी `

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}

(अन्य चर के बीच) FirstViewController.h । क्या यह सही नहीं है?

यहां मेरा cellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"hallo";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil)
{
    cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

NSMutableArray *marr = [hallo objectAtIndex:indexPath.section];
NSDictionary *dict = [marr objectAtIndex:indexPath.row];

NSString* imageName = [dict objectForKey:@"Image"];
//NSLog(@"url: %@", imageURL);

UIImage *image = [_imageCache objectForKey:imageName];

if(image)
{
    cell.imageView.image = image;
}
else
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSString* imageURLString = [NSString stringWithFormat:@"example.com/%@", imageName];
        NSURL *imageURL = [NSURL URLWithString:imageURLString];
        UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];

        if(image)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                CustomCell *cell =(CustomCell*)[self.tableView cellForRowAtIndexPath:indexPath];
                if(cell)
                {
                    cell.imageView.image = image;
                }
            });
            [_imageCache setObject:image forKey:imageName];
        }
    });
}

cell.textLabel.text = [dict objectForKey:@"Name"];

return cell;
}

कैशिंग छवियों को इस तरह से किया जा सकता है।

ImageService.m

@implementation ImageService{
    NSCache * Cache;
}

const NSString * imageCacheKeyPrefix = @"Image-";

-(id) init {
    self = [super init];
    if(self) {
        Cache = [[NSCache alloc] init];
    }
    return self;
}

/** 
 * Get Image from cache first and if not then get from server
 * 
 **/

- (void) getImage: (NSString *) key
        imagePath: (NSString *) imagePath
       completion: (void (^)(UIImage * image)) handler
    {
    UIImage * image = [Cache objectForKey: key];

    if( ! image || imagePath == nil || ! [imagePath length])
    {
        image = NOIMAGE;  // Macro (UIImage*) for no image 

        [Cache setObject:image forKey: key];

        dispatch_async(dispatch_get_main_queue(), ^(void){
            handler(image);
        });
    }
    else
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul ),^(void){

            UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[imagePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];

            if( !image)
            {
                image = NOIMAGE;
            }

            [Cache setObject:image forKey: key];

            dispatch_async(dispatch_get_main_queue(), ^(void){
                handler(image);
            });
        });
    }
}


- (void) getUserImage: (NSString *) userId
           completion: (void (^)(UIImage * image)) handler
{
    [self getImage: [NSString stringWithFormat: @"%@user-%@", imageCacheKeyPrefix, userId]
         imagePath: [NSString stringWithFormat: @"http://graph.facebook.com/%@/picture?type=square", userId]
        completion: handler];
}

SomeViewController.m

    [imageService getUserImage: userId
                    completion: ^(UIImage *image) {
            annotationImage.image = image;
    }];

मुझे लगता है कि आपके लिए उपयोगकर्ता DLImageLoader जैसे कुछ बेहतर होगा। अधिक जानकारी -> https://github.com/AndreyLunevich/DLImageLoader-iOS

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            imageView.image = image;
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];

मुझे लगता है कि कालेब ने कैशिंग सवाल का जवाब दिया। जब आप छवियों को पुनर्प्राप्त करते हैं, तो मैं आपके UI को अपडेट करने की प्रक्रिया पर स्पर्श करने जा रहा था, उदाहरण के लिए कि आपके पास NSCache नामक आपकी छवियों के लिए _imageCache :

सबसे पहले, तालिकादृश्य के लिए एक ऑपरेशन कतार संपत्ति परिभाषित करें:

@property (nonatomic, strong) NSOperationQueue *queue;

फिर viewDidLoad , इसे प्रारंभ करें:

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;

और फिर cellForRowAtIndexPath , आप तब कर सकते हैं:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ilvcCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // set the various cell properties

    // now update the cell image

    NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved

    UIImage *image = [_imageCache objectForKey:imagename];

    if (image)
    {
        // if we have an cachedImage sitting in memory already, then use it

        cell.imageView.image = image;
    }
    else
    {
        cell.imageView.image = [UIImage imageNamed:@"blank_cell_image.png"];

        // the get the image in the background

        [self.queue addOperationWithBlock:^{

            // get the UIImage

            UIImage *image = [self getImage:imagename];

            // if we found it, then update UI

            if (image)
            {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // if the cell is visible, then set the image

                    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                    if (cell)
                        cell.imageView.image = image;
                }];

                [_imageCache setObject:image forKey:imagename];
            }
        }];
    }

    return cell;
}

मैं केवल इसका उल्लेख करता हूं क्योंकि मैंने हाल ही में एसओ पर तैरने वाले कुछ कोड नमूने देखे हैं जो उचित UIImageView image प्रॉपर्टी को अपडेट करने के लिए जीसीडी का उपयोग करते हैं, लेकिन मुख्य कतार में यूआई अपडेट को प्रेषित करने की प्रक्रिया में, वे उत्सुक तकनीकों को लागू करते हैं (उदाहरण के लिए , सेल या टेबल को फिर से लोड करना, तालिका के शीर्ष पर लौटाए गए मौजूदा cell ऑब्जेक्ट की छवि प्रॉपर्टी को अपडेट करना देखें tableView:cellForRowAtIndexPath सेलफोररोएट इंडेक्सपैथ (यदि समस्या स्क्रीन से स्क्रॉल हो गई है और सेल को अस्वीकार कर दिया गया है और इसका पुन: उपयोग किया जा रहा है एक नई पंक्ति), आदि)। cellForRowAtIndexPath का उपयोग करके ( tableView:cellForRowAtIndexPath साथ भ्रमित न होने के लिए), आप यह निर्धारित कर सकते हैं कि सेल अभी भी दिखाई दे रहा है और / या यदि यह स्क्रॉल हो गया हो और उसे हटा दिया गया हो और पुन: उपयोग किया गया हो।


////.h file

#import <UIKit/UIKit.h>

@interface UIImageView (KJ_Imageview_WebCache)


-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image;

@end

//.m file


#import "UIImageView+KJ_Imageview_WebCache.h"


@implementation UIImageView (KJ_Imageview_WebCache)




-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image
{




    NSString *imageUrlString = urlString;
    NSURL *url = [NSURL URLWithString:urlString];



    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:[self tream_char:urlString]];

    NSLog(@"getImagePath--->%@",getImagePath);

    UIImage *customImage = [UIImage imageWithContentsOfFile:getImagePath];




    if (customImage)
    {
        self.image = customImage;


        return;
    }
    else
    {
         self.image=placeholder_image;
    }


    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *uploadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {


        if (error)
        {
            NSLog(@"%@",[error localizedDescription]);

           self.image=placeholder_image;

            return ;
        }

        dispatch_async(dispatch_get_main_queue(), ^{


            UIImage *imageToCache = [UIImage imageWithData:data];



            if (imageUrlString == urlString)
            {

                self.image = imageToCache;
            }



            [self saveImage:data ImageString:[self tream_char:urlString]];

        });




    }];

    [uploadTask resume];



}


-(NSString *)tream_char :(NSString *)string
{
    NSString *unfilteredString =string;

    NSCharacterSet *notAllowedChars = [[NSCharacterSet characterSetWithCharactersInString:@"[email protected]#$%^&*()_+|abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"] invertedSet];
    NSString *resultString = [[unfilteredString componentsSeparatedByCharactersInSet:notAllowedChars] componentsJoinedByString:@""];
    NSLog (@"Result: %@", resultString);



    return resultString;

}

-(void)saveImage : (NSData *)Imagedata ImageString : (NSString *)imageString
{

    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:imageString];




    if (![Imagedata writeToFile:documentDirectoryFilename atomically:NO])
    {
        NSLog((@"Failed to cache image data to disk"));
    }
    else
    {
        NSLog(@"the cachedImagedPath is %@",documentDirectoryFilename);
    }

}

@end


/// call 

 [cell.ProductImage loadImageUsingUrlString:[[ArrProductList objectAtIndex:indexPath.row] valueForKey:@"product_image"] placeholder:[UIImage imageNamed:@"app_placeholder"]];




load