how to manage caching memory in AFNetworking Library - ios

I am developing a content display app in which there is a table view and a detail view corresponding to each row in the table.
There are 12 categories in which content is loaded.
I have completed the app and it is working fine. Now I need to manage memory consumption for cache as I am receiving a warning at run time. I am using AFNetowking lib for caching.
There is no problem in functioning of the app. I just need to do some memory management and apply the code.
I am trying to allocate some particular memory and disk apace for each category.
following is the code that i am using to allocate ram and disk size for each category.
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:15 * 512 * 1024
diskCapacity:10 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
xcode version : 6.1
taget ios version : 6.0
app : universal

I think the recommendation is to not set the size of the cache. The system should take care of that.
I would generally use a common NSCache, and just store data packets based on the URL. And then convert the data back into any image/xml/text whatever I was expecting.
// This should reference an NSCache instance
_contentCache = [[NSCache alloc] init];
// create a key for the request.
cacheKey = [NSString stringWithFormat:#"%#|%#",uri,body];
NSData *cacheData = [_contentCache objectForKey:cacheKey];
// if its in the cache, just return it.
if (cacheData) {
return cacheData;
}
// Request the data here and return it.
_contentCache is defined in the AppDelegate. So that I can reference it easily from across the app.
- (id) init {
_appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
_contentCache = [_appDelegate contentCache];
return self;
}
I've posted my class to GitHub. Its not complete, but its useable.
postmaster - GitHub

Related

How to reinit AVPlayerItem?

I am initializing AVPlayerItem with "initWithUrl:" method.
Issue is that when initialization is interrupted (e.g. connection is lost)
Let's say we have following:
self.avPlayerItem = [[AVPlayerItem alloc] initWithUrl:url];
1.
What should be done? What happens to instance of avPlayerItem when connection is lost meanwhile?
Is there some protocol to implement in order to identify if initialization was successful or not?
2.
What I have noticed is that once initialization is interrupted then next "successful" initialization of avPlayerItem doesn't have tracks at all (they are present when no interruption is done before)
To initialize avPlayerItem with tracks again from the source user needs to close and open the app again.
When dealing with code that relies on internet connection you have to make sure there are failsafes that kick in when something goes wrong, because at some point it will.
What should be done?
1) Perform all code that relies on internet connection on a background thread. You don't want to block up the UI.
2) If possible verify the downloaded file before using.
There are different ways of going about this but off the top of my head I think I would use NSURLConnection or similar API to download the file into a temp folder. ON A BACKGROUND THREAD.
When downloaded I would initialise an AVAsset using the temp URL. AVAsset has some nice properties like playable that will help you check that the file downloaded OK. (NSURLConnectionDelegate also has a method that notifies if there was an error downloading.)
If you've got this far then you can create an AVPlayerItem with your AVAsset and away you go. Remember to wipe the contents of your temp folder at some point if you're not hanging on to the downloaded content.
Remember that you want to play your file on the main thread but all other loading and checking is probably best done on a background thread; you definitely want to use NSURLConnection from a background thread.
Store the AVPlayerItem in an NSPurgeableData object first, and play from that; store the data object in an NSCache object to automatically evict the object from memory after it has played, or when the connection is dropped, and the former AVPlayerItem is replaced by a new one (all of these things you should be doing anyway, regardless of the particular problem you describe). Here's some code to get you started:
void (^cachePlayerItem)(AVPlayerItem *, NSCache *, NSString *) = ^(AVPlayerItem *playerItem, NSCache *cache, NSString *key) {
NSURL *fileURL = [(AVURLAsset *)playerItem.asset URL];
NSPurgeableData *data = [NSPurgeableData dataWithContentsOfURL:fileURL];
[data beginContentAccess];
[cache setObject:data forKey:key];
[data endContentAccess];
[data discardContentIfPossible];
};
Put this block anywhere in an implementation file, defining it in the header file with:
typedef void (^cachePlayerItemBlock)(AVPlayerItem *, NSCache *, NSString *);
Call it within a method with:
cachePlayerItem(playerItem, playerItems, phAsset.localIdentifier);
Whereas, playerItem is the AVPlayerItem, playerItems is the NSCache cache, and, depending on what kind of asset from which you are acquiring the AVPlayerItem, an identifier of some kind unique to it (or, in the example above, its associated asset).
By the way, I set up my caches in AppDelegate accordingly:
- (NSCache *)thumbnailCache {
__block NSCache *t = self->_thumbnailCache;
if (!t) {
t = [[NSCache alloc] init];
[t setName:#"Thumbnail Cache"];
[t setEvictsObjectsWithDiscardedContent:TRUE];
[t setCountLimit:self.assetsFetchResults.count];
self->_thumbnailCache = t;
}
return t;
}
This ensures not only global access to, but also one instance of, the cache, which is particularly important when caching AVPlayerItems, as they can be large in size.
To create a globally accessible cache in AppDelegate, add these to your header and implementation files, respectively:
+ (AppDelegate *)sharedAppDelegate;
...and:
+ (AppDelegate *)sharedAppDelegate
{
return (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
To reference the cache in other class files, import the AppDelegate header file, and, if desired, create a shortcut to the AppDelegate's sharedApplication method:
#import "AppDelegate.h"
#define AppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Insodoing, the cache can be referenced by...:
AppDelegate.thumbnailCache
...instead of:
AppDelegate.sharedAppDelegate.thumbnailCache

Unable to fetch data faster in cloudant DB

I was using cloud ant Db for my iOS app with name tasks_master with almost 1000 documents. When I tried to fetch data from cloud ant it's taking almost 30 sec to fetch data i have tried with below code.
- (NSURL*) replicatorURL {
AppDelegate *app = [[UIApplication sharedApplication]delegate];
NSString *db_name = #"tasks_master";
NSString *url = [NSString stringWithFormat:#"https://%#:%##%#.cloudant.com/%#",
username,
password,
username,db_name];
return [NSURL URLWithString:url];
}
- (void) sync:(UIViewController *)sender {
[self pullReplication:sender];
[self pushReplication:sender];
}
-(void)pullReplication:(UIViewController *)sender {
[self log:#"Starting pull replication"];
NSURL *url = [self replicatorURL];
AppDelegate *delegate1 = (AppDelegate *)[[UIApplication sharedApplication] delegate];
CDTReplicatorFactory *factory = delegate1.replicatorFactory;
CDTReplicator *replicator = [factory onewaySourceURI:url targetDatastore:delegate1.datastore];
[self startAndFollowReplicator:replicator label:#"pull"];
}
- (void) pushReplication:(UIViewController *)sender {
[self log:#"Starting push replication"];
NSURL *url = [self replicatorURL];
AppDelegate *delegate1 = (AppDelegate *)[[UIApplication sharedApplication] delegate];
CDTReplicatorFactory *factory = delegate1.replicatorFactory;
CDTReplicator *replicator = [factory onewaySourceDatastore:delegate1.datastore targetURI:url];
[self startAndFollowReplicator:replicator label:#"push"];
}
when i call tasks
-(void)fetchtasks{
[[[CDTTodoReplicator alloc]init]sync];
self.indexManager = [[CDTIndexManager alloc] initWithDatastore:self.datastore
error:&error];
indexName= [self.datastore ensureIndexed:#[#"originator",#"members",#"meeting_status"] withName:#"meeting_index"];
query=#{#"$and":#[#{#"meeting_status":#"Scheduled"}, #{#"$or":#[#{#"originator":app.userName},#{#"members":app.userName}]}]};
result = [self.datastore find:query];
}
can any one help me out how to fetch data faster .
I've written a little helper class to cut down on some of the boiler plate for this kind of thing. It's the one used in the video referenced above.
Cloudant.h https://gist.github.com/xpqz/f8b304353080c3963a45
Cloudant.m https://gist.github.com/xpqz/62b5267c250f04c30f9b
When you add documents to a Cloudant database, they become available through the so called primary index, also known as All Docs. This can easily be checked with 'curl' from the command line, e.g.:
% curl https://skruger.cloudant.com/routes/_all_docs
Every document you store will appear in the returned data from the primary index.
Design documents are there for a different purpose - they define secondary indexes, called views, mostly written as little javascript functions that define a map-reduce operation to expose some facet of your data. Views allow you to index your documents in additional ways to the document id that you get for free.
You can create your own views directly by creating design documents yourself, but client software accessing the database sometimes also create design documents and views automatically which sometimes can be confusing when you see stuff appearing under _design that you didn't explicitly create.
In summary: every document appear in the primary index, _all_docs. Design documents are there to hold javascript functions used to generate secondary indexes, called views. These can be created explicitly, or generated automatically 'behind your back' by client software.
More info:
Primary index and all docs
Design documents and secondary indexes
Search indexes and Lucene
Stefan

ios - Storing binary data 'externally' in coredata causes orphaned files

I'm not sure whether this is a bug or not, so am asking for advice...
(iOS 7.1.2 on xcode 5.1.1)
My app stores many large data image in coredata. The binary images have their attribute set in the entity to 'Allows External Storage', so I see a file (guid) appear in the _EXTERNAL_DATA sub-folder for my app.
During the lifetime of this app, the file will change regularly and so I overwrite the existing image and save the context.
The problem is, I'm seeing orphaned copies of my image files (guids) appearing, as new ones are created, but the old ones are not deleted.
This can be reproduced as follows...
Create a utility app with a 'test' button on it that utilises coredata, creating a simple entity...
Create the initial entity in the viewDidLoad, storing a reference to it....
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
id delegate = [[UIApplication sharedApplication]delegate];
NSManagedObjectContext *managedObjectContext = [delegate managedObjectContext];
// Create initial external file
_testEntity = [NSEntityDescription insertNewObjectForEntityForName:#"TestEntity" inManagedObjectContext:managedObjectContext];
UIImage *planeImage = [UIImage imageNamed:#"plane.jpg"];
_testEntity.image = [NSData dataWithData:UIImagePNGRepresentation(planeImage)];
[delegate saveContext];
}
Then in an action handler for a button on the view, simply change the image...
-(IBAction)onTestImageButton:(id)sender {
int randNum = rand() % 4 + 1;
id delegate = [[UIApplication sharedApplication]delegate];
// Store image - again
UIImage *planeImage = [UIImage imageNamed:[NSString stringWithFormat:#"plane %d.jpg", randNum]];
_testEntity.image = [NSData dataWithData:UIImagePNGRepresentation(planeImage)];
[delegate saveContext]; }
Here, I have four large jpg's of a plane, each one slightly different size. (If they are the same size, the problem doesn't manifest itself)
Run the app and press the 'test' button several times. Soon, several versions of the file appear in _EXTERNAL_DATA
I would only ever expect there to be one version. Images are now orphaned and if a parent entity deletes this one via cascade delete rules, files are left behind, which take valuable space!
Is this a bug, or am I doing something wrong?
Thanks

How to use SDURLCache and AFNetworking

i'm beginner.i have a problem about SDURLCache and AFNetwoking.my code:
first,i setting sdurlcache in my appdelegate.m
self.cache = [[SDURLCache alloc] initWithMemoryCapacity:1024*1024 // 1 MB mem cache
diskCapacity:1024*1024*5 // 5 MB disk cache
diskPath:[SDURLCache defaultCachePath]];
self.cache.ignoreMemoryOnlyStoragePolicy = YES;
[NSURLCache setSharedURLCache:self.cache];
second,i used AFNetwoking+UIImageView in my tableCell:
[cell.Image setImageWithURL:url placeholderImage:[UIImage imageNamed:#"zhanweiSmall.png"]];
But picture not cache.if network is invalid,picture don't show
If you are using IOS5 or above NSURLCache automatically saves responses to disk. So you don't need SDURLCache. See Peter's blog post for more info.
http://petersteinberger.com/blog/2012/nsurlcache-uses-a-disk-cache-as-of-ios5/

How do I stop text fields and picked images vanishing when device is turned off

My text fields and my images picked from image picker all reset set blank if my app stops running, or device is turned off, How can I retain this information?
I've used a singleton (with help from a fellow member) and I can retain my image...that is until the app is killed or device is turned off. Then it's gone.
.m
- (void)viewDidLoad
{
singletonObj = [Singleton sharedSingletonController];
imageView.image = singletonObj.imagePicked;
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[self setImageView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#pragma mark - Action
- (IBAction)done:(id)sender
{
[self.delegate flipsideViewControllerDidFinish:self];
}
- (IBAction)btn:(id)sender {
UIImagePickerController * picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
if((UIButton *) sender == choosePhotoBtn) {
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
[self presentModalViewController:picker animated:YES];
}
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
NSData *dataImage = UIImageJPEGRepresentation([info objectForKey:#"UIImagePickerControllerOriginalImage"],1);
UIImage *img = [[UIImage alloc] initWithData:dataImage];
singletonObj.imagePicked = img;
imageView.image = img;
[picker dismissModalViewControllerAnimated:YES];
}
#end
There are two types of memory: volatile (RAM) and permanent memory (ie: hard drives and other storage).
Volatile memory is cleared and lost when a program/computer shuts down.
Using a singleton is fine but it's completely unrelated to keeping data from session to session (and by session I mean the time when the program is running: from launch to termination of an application).
You need to store data you wish to keep from session to session to file using any method you want. Depending on the information you want to store, there are different dedicated mechanism for saving:
(such as NSUserDefaults for user preferences).
Core Data is a framework which defines mechanism for structuring data and saving/reading it to file (a.k.a. persistent store).
You can also use serialization.
Or you can always manually manipulate files.
NSData has writeToFile:atomically: which will write create a file out of a data object. If you want to save an image, you must obtain an UIImage's underlying data (i.e.: UIImagePNGRepresentation(...)).
You are going to have to use core data. It can accept NSData from a UIIimage as well as NSStrings. The function within the appDelegate, AppicationWillTerminate, will have to use so that the information is stored just before the application is terminated.
This is going to require some decent amount of work to get it working properly, but nothing too difficult. If you need help understanding core data, I recommend this link
http://www.raywenderlich.com/934/core-data-on-ios-5-tutorial-getting-started

Resources