Thread safety of NSMutableDictionary access and destruction - ios

I have an application that downloads information from a web service and caches it in memory. Specifically, my singleton cache class contains an instance variable NSMutableDictionary *memoryDirectory which contains all of the cached data. The data in this cache can be redownloaded easily, so when I receive a UIApplicationDidReceiveMemoryWarningNotification I call a method to simply invoke
- (void) dumpCache:(NSNotification *)notification
{
memoryDirectory = nil;
}
I’m a little worried about the thread safety here. (I’ll admit I don’t know much about threads in general, much less in Cocoa’s implementation.) The cache is a mutable dictionary whose values are mutable dictionaries, so there are two levels of keys to access data. When I write to the cache I do something like this:
- (void) addDataToCache:(NSData *)data
forKey:(NSString *)
subkey:(NSString *)subkey
{
if (!memoryDirectory)
memoryDirectory = [[NSMutableDictionary alloc] init];
NSMutableDictionary *methodDictionary = [memoryDirectory objectForKey:key];
if (!methodDictionary) {
[memoryDirectory setObject:[NSMutableDictionary dictionary] forKey:key];
methodDictionary = [memoryDirectory objectForKey:key];
}
[methodDictionary setObject:data forKey:subkey];
}
I’m worried that sometime in the middle of the process, dumpCache: is going to nil out the dictionary and I’m going to be left doing a bunch of setObject:forKey:s that don’t do anything. This isn’t fatal but you can imagine the problems that might come up if this happens while I’m reading the cache.
Is it sufficient to wrap all of my cache reads and writes in some kind of #synchronized block? If so, what should it look like? (And should my dumpCache: be similarly wrapped?) If not, how should I ensure that what I’m doing is safe?

Instead of using an NSMutableDictionary, consider using NSCache, which is thread safe. See this answer for example usage. Good luck!

Related

Fully dealloc NSMutableDictionary / NSObject

I am retrieving a tremendous amount of data from a server and I have to quickly initialize and deallocate a NSMutableDictionary.
I would use core data but requirements say I have to use one allocated object for fast storage of incoming JSON data, save it to NSUserDefaults and completely remove it. This will be happening multiple times in a background process over a period of time.
Tests were on a live iPhone 6S (not a simulator)
I tested a completely bare "Single View Application" that resulted with:
As you can see a bare bones "Single View Application" consumes 9MB of memory.
I then ran a test on a NSMutableDictionary. My test was to initialize with data, remove all objects, and restore the RAM.
The dictionary stored 10,000 tiny values (in viewDidLoad - for testing purposes):
_dictionary = [[NSMutableDictionary alloc] initWithDictionary:#{
#"Item00000" : [NSNumber numberWithInt:0],
...
#"Item10000" : [NSNumber numberWithInt:10000]}];
After initializing - I waited two seconds and removed all objects and attempted to remove the NSMutableDictionary.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_largeDictionary removeAllObjects];
_largeDictionary = nil;
});
But the memory consumption did not show the NSMutableDictionary getting removed:
Does anyone have a solution to this problem? I thought this was taken care of when Apple depreciated the autorelease method. My live environment is quickly initializing relatively large chunks of data per key-value pair - then deallocating and preparing for the next chunk. The next chunk may be stored in a different NSMutableDictionary. That's why it is necessary for me to completely remove the first NSMutableDictionary from memory. I have seen many answers pertaining to this question but none more recent than 2013.
Update: this #property is: (nonatomic, strong)
Thank you in advance.

Objective-C iOS: Begin populating UITableView immediately from JSON Stream

I have a UITableView with cells to display images and text from a largish (5000 items) JSON file. I want to stream the JSON in and start updating the UITableView immediately, but can't seem to work out the plumbing for this.
- (NSArray *)parseJSONIntoImageObjectsFromData:(NSData *)rawJSONData {
NSError *error;
NSMutableArray *arrayOfImageObjects = [[NSMutableArray alloc] init];
NSURL *myURL = [[NSURL alloc] initWithString:self.urlString];
NSData *objects = [NSData dataWithContentsOfURL:myURL];
NSInputStream *stream = [[NSInputStream alloc] initWithData:objects];
[stream open];
NSMutableArray *arrayFromStream = [NSJSONSerialization JSONObjectWithStream:stream options:NSJSONReadingAllowFragments error:&error];
for (NSDictionary *JSonDictionary in arrayFromStream) {
NSLog(#"Count is %lu", (unsigned long)arrayOfImageObjects.count);
NSInteger imgID = (NSInteger)JSonDictionary[#"id"];
ImageObject *newImageObject = [[ImageObject alloc] initWithID:imgID andTitle:JSonDictionary[#"title"] andThumbnailURL:JSonDictionary[#"thumbnailUrl"]];
[arrayOfImageObjects addObject:newImageObject];
}
return arrayOfImageObjects;
}
This definitely gets them as a stream, as the NSLog reveals in the debug window. But since it waits for the return it has to complete. I'm a little puzzled at going about this and can't find a good code sample. Do I perhaps return a stream?
EDIT: I am not terribly concerned about the brief delay I am encountering and I am sure the delay is more on the retrieval than in the parsing, I just want to learn to retrieve the data as a stream and update the UITableView incrementally as a way to do this better. I enjoy working on data retrieval and manipulation and am trying to improve my skills by knowing more.
Also, the images are retrieved asynchronously at display time using an NSOperationQueue and don't really matter for this task.
If you benchmark this, I think you'll find that the parsing time of the JSON is inconsequential. The slow parts are going to be the download of the original JSON (and possibly the creation of the ImageObject objects). You should benchmark this in Instruments (use the "time profiler" tool) and use the "record waiting threads" option. See WWDC video Building Concurrent User Interfaces on iOS for a demonstration on how to use Instruments to diagnose these sorts of issues.
I would first retire the dataWithContentsOfURL, as that runs synchronously. I would advise using an asynchronous technique such as NSURLSession method dataWithURL (or if you need support for pre-iOS 7, NSURLConnection method sendAsynchronousRequest).
Usually in these cases, the JSON is small enough, that the biggest delay stems from the network latency in making the initial request. I mention that so that you don't bother embarking on some major refactoring of the code for paging/streaming approaches without confirming that this will solve the problem.
Also, you haven't shared this ImageObject logic, but if that is synchronously loading images, that's a likely candidate for refactoring for asynchronous retrieval, too. Without knowing more about that class, it's hard to advise you further on that point.
Define NSMutableArray *arrayOfImageObjectsas a property or variable outside this method and then in your for loop, call [self.tableView reloadData] after maybe every 100 objects.
That's assuming that your numberOfRowsInSection is keying off of arrayOfImageObjects as well and cellForRowAtIndexPath is using it to populate the table data.
But also consider 'paging' your data, so as to only load 50 objects or so, at once (assuming your API supports this like 'http://example.com/imagedata?page=1'). Then if the user flicks or scrolls the tableview you can do another api call, increasing the page number and adding that new set of data to your current set and calling reloadData.
EDIT: also I'm assuming your "parseJSONIntoImageObjectsFromData" is running asynchronously. If not then use something like AFNetworking (or sendAsynchronousRequest:queue:completionHandler: in NSURLConnection) and in the completion block you can start adding to your array.

A bit help for using queuing and Grand Central Dispatch on iOS

This is my problem, I'm building an iOS with some JSON returs from my server, here, no problems, all works fine.
The problem is that when I run the program, it take a long long time to parse the result into a NSMutableArray: this is the log
2013-01-10 12:03:48.627 project[5917:907] <- time begin parser
2013-01-10 12:03:48.755 project[5917:907] <- time finish parser
2013-01-10 12:03:48.756 project[5917:907] <- time begin implement arrays
2013-01-10 12:03:58.522 project[5917:907] <- time finish implement array
As you can see, implementing the arrays is really long.
I know that I have to use queueing and grand central dispatch to make my UI responsive, but I don't know how to, could you please help me to do that ?
This is my viewDidLoad Method
- (void)viewDidLoad
{
[super viewDidLoad];
if debug
NSLog(#"<- time begin parser");
endif
NSString *URLStr = #"http://myJsonFile.json";
NSDictionary *myDictwithReturn = [TOCJSONParser awesomeParserWithStringOfYourJSONFile:URLStr]; //really cool parser, i can put it on gitHub if you want
NSArray *speakersArray = [myDictwithReturn objectForKey:#"speakers"];
myArray = [[NSMutableArray alloc]init];
NSLog(#"<- time finish parser");
NSLog(#"<- time begin implement arrays");
for (NSDictionary *myDict in speakersArray) {
_nextSpeaker = [[TOCSpk alloc]init];
[_nextSpeaker setName:[myDict objectForKey:#"name"]];
[_nextSpeaker setBusiness:[myDict objectForKey:#"business"]];
[_nextSpeaker setDesc:[myDict objectForKey:#"desc"]];
[_nextSpeaker setTwitter:[NSURL URLWithString:[myDict objectForKey:#"twitter"]]];
[_nextSpeaker setPicture:[_nextSpeaker retrieveImageFromServer:[myDict objectForKey:#"picture"]]];
[myArray addObject:_nextSpeaker];
}
NSLog(#"<- time finish implement array");
}
I suspect that the slowness comes from calling retrieveImageFromServer, which lets me think that you are accessing the network. If that access is synchronous, as it seems from the fact that you are assigning the image in the same statement, than this is bound to be slow.
You should review your code and make it run on a separate thread or use asynchronous network access.
EDIT:
After your comment about using, dataWithContentsOfURL, my above hypothesis is confirmed.
You can read this S.O. post about a way to download images asynchronously, or you might use any of various networking frameworks available out there.
Possibly, the easiest path forward is using SDWebImage, which is a class that offers async download for images, so you don´t have to bother yourself with thread management or NSURLConnection:
Just #import the UIImageView+WebCache.h header, and call the setImageWithURL:placeholderImage: method from the tableView:cellForRowAtIndexPath: UITableViewDataSource method. Everything will be handled for you, from async downloads to caching management.

Thread safety crash using NSDictionary?

I have code accessing and setting an NSDictionary many times across multiple threads, like so:
- (BOOL)flagForItem:(NSNumber*)itemID
{
if(itemID) {
return [[_itemFlagDict objectForKey:itemID] boolValue]
}
return NO;
}
and:
- (void)setFlagForItem:(NSNumber*)itemID
{
if(itemID) {
NSMutableDictionary *copy = [_itemFlagDict mutableCopy];
[copy setObject:[NSNumber numberWithBool:YES] forKey:itemID];
_itemFlagDict = [NSDictionary dictionaryWithDictionary:copy];
}
}
In the set method, I originally had a NSMutableDictionary - this was changed to the pattern you see now because, doh, NSMutableDictionary isn't threadsafe. My reasoning was to perform the mutation in a copy, and then reassign the _itemFlagDict to capture the update.
However, occasionally a EXC_BAD_ACCESS crash still occurs when accessing the _itemFlagDict, leading me to believe that the dictionary is reassigned WHILE the accessing the objectForKey:itemID.
One other approach I tried was to use #synchronized(_itemFlagDict) on both the accessor and the setter methods. While this fixed the issue, this code is performance sensitive and synchronizing the access/assignment caused too much performance degradation.
So my question is, are there other patterns/methods I can use to avoid this bad access while not compromising performance? If prioritization matters, the execution (and not necessarily iron-clad accuracy) of the accessor method is most important.
Note: i'm working with iOS 4 and above
Have you tried read/write locks ? You can have multiple threads in your get methods and one writer in the set method https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/pthread.3.html
I have encountered the same problem, and my current solution is use Atomic operation to implement a thread-safe dictionary.
You can check out it: https://github.com/bangquangvn/FastestThreadsafeDictionary-iOS
I think it's the fastest solution.

Modifying mutable object in completion handler

I have a question about thread safety of the following code example from Apple (from GameKit programming guide)
This is to load achievements from game center and save it locally:
Step 1) Add a mutable dictionary property to your class that report achievements. This dictionary stores the collection of achievement objects.
#property(nonatomic, retain) NSMutableDictionary *achievementsDictionary;
Step 2) Initialize the achievements dictionary.
achievementsDictionary = [[NSMutableDictionary alloc] init];
Step 3) Modify your code that loads loads achievement data to add the achievement objects to the dictionary.
{
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error)
{
if (error == nil)
{
for (GKAchievement* achievement in achievements)
[achievementsDictionary setObject: achievement forKey: achievement.identifier];
}
}];
My question is as follows - achievementsDictionary object is being modified in the completion handler, without any locks of sort. Is this allowed because completion handlers are a block of work that will be guaranteed by iOS to be executed as unit on the main thread? And never run into thread safety issues?
In another Apple sample code (GKTapper), this part is handled differently:
#property (retain) NSMutableDictionary* earnedAchievementCache; // note this is atomic
Then in the handler:
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache= [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache= tempCache;
}
}];
So why the different style, and is one way more correct than the other?
Is this allowed because completion handlers are a block of work that will be guaranteed by iOS to be executed as unit on the main thread? And never run into thread safety issues?
This is definitely not the case here. The documentation for -loadAchievementsWithCompletionHandler: explicitly warns that the completion handler might be called on a thread other than the one you started the load from.
Apple's "Threading Programming Guide" classifies NSMutableDictionary among thread-unsafe classes, but qualifies this with, "In most cases, you can use these classes from any thread as long as you use them from only one thread at a time."
So, if both apps are designed such that nothing will be working with the achievement cache till the worker task has finished updating it, then no synchronization would be necessary. This is the only way in which I can see the first example as being safe, and it's a tenuous safety.
The latter example looks like it's relying on the atomic property support to make the switcheroo between the old cache and the new cache. This should be safe, provided all access to the property is via its accessors rather than direct ivar access. This is because the accessors are synchronized with respect to each other, so you do not risk seeing a half-set value. Also, the getter retains and autoreleases the returned value, so that code with the old version will be able to finish working with it without crashing because it was released in the middle of its work. A nonatomic getter simply returns the object directly, which means that it could be deallocated out from under your code if a new value were set for that property by another thread. Direct ivar access can run into the same problem.
I would say the latter example is both correct and elegant, though perhaps a bit over-subtle without a comment explaining how crucial the atomicity of the property is.

Resources