I am facing a strange crash where an instance of NSNumber seems to be deallocated although it persists in array. I have created a system to download multiple files from remote server and have a block to indicate progress (an average progress really). And the computation of the progress produces a crash. The crash is not consistent and happens "usually" at can occur at any point. [_NSProgressFractionTuple floatValue]: unrecognized selector sent to instance 0x17042ab80 leads me to believe the NSNumber is somehow deallocated and I fail to see how this is possible.
To give the full method code:
- (void)downloadFilesFromURLs:(NSArray<NSString *> *)urlPaths withProgress:(void (^)(float progress))progressBlock completion:(void (^)(NSError *error))completionBlock {
NSMutableArray *toLoad = [[NSMutableArray alloc] init];
for(NSString *path in urlPaths) {
if([self fileForURL:path] == nil) {
[toLoad addObject:path];
}
}
NSInteger itemsToLoad = toLoad.count;
if(itemsToLoad <= 0) {
if(completionBlock) {
completionBlock(nil);
}
return;
}
// Set progresses to zero
__block NSMutableArray *progresses = [[NSMutableArray alloc] init];
for(int i=0; i<itemsToLoad; i++) [progresses addObject:[[NSNumber alloc] initWithFloat:.0f]];
__block NSInteger requestsOut = itemsToLoad;
__block NSError *latestError = nil;
for(int i=0; i<itemsToLoad; i++) {
NSInteger index = i;
[self downloadFileFromURL:toLoad[index] named:nil withProgress:^(float progress) {
progresses[index] = [[NSNumber alloc] initWithFloat:progress];
if(progressBlock) {
float overallProgress = .0f;
for(NSNumber *number in [progresses copy]) {
overallProgress += number.floatValue;
}
progressBlock(overallProgress/itemsToLoad);
}
} completion:^(NSString *filePath, NSError *error) {
if(error) latestError = error;
requestsOut -= 1;
if(requestsOut <= 0) {
if(completionBlock) {
completionBlock(latestError);
}
}
}];
}
}
Code explanation:
So this method accepts an array of URLs. It then checks if some of the files were already downloaded and creates a new array which only contains URLs that need to be downloaded. If all files exist or no URLs are provided then the completion is called and the operation breaks.
Next I create a mutable array and fill it with NSNumber instances all having a zero value. I remember how many requests will be made and I create a placeholder for an error. I iterate through all the URLs and initialize requests where each will report a progress and completion and both of these are on a main thread.
So in progress block I access the array of progresses to assign the new values through indexing. I then compute an average progress and report overall progress to an input block.
The request completion decreases the number of requests counter and when that one falls to zero the input completion is called.
The situation:
It all works as expected, the whole procedure is correct. The given values are all valid and all the files are there on the server and are accessible. When the app does not crash it all works as it should.
But when it crashes it crashes in
for(NSNumber *number in [progresses copy]) {
overallProgress += number.floatValue;
}
and the crash is random but in any case the number.floatValue seems to be accessing a memory that it shouldn't.
I now have a solution where I replaced the progresses array with pure C pointer float *progresses = malloc(sizeof(float)*itemsToLoad); which is freed on completion. It seems to work but still, what am I missing here? What could be the cause of array with NSNumbers not working here?
Some additional info:
Memory is OK, this is writing directly into files and even if it didn't the overall file size is relatively small
Disk space is OK
I was using #(progress) syntax but changed it to explicit allocation in hopes of removing the issue
progresses does not need __block, I added it just in case
Completion does not get called before all the progresses get called and even if it did I see no reason to crash the app
Thank you!
NSMutableArray is not thread safe. So even though there is no explicit memory management issue, if NSMutableArray is accessed at the same time by two different thread bad things can happen. I believe that dispatching the withProgress block in a serial queue would solve the issue.
I have an array of PFFiles I retrieve from parse. The PFFiles must be converted to images and I am trying to convert them in a loop, however;
The array of converted images must be in the same order of the array containing the PFFiles.
The problem is, is the loop runs and causes all of the blocks to trigger, then they are all loading at the same time thus the imageArray will result in a different order to the original array of PFFiles, because if one object finishes loading before the previous object, it will be added to the array before the previous one, making it go out of order.
Instead, I would like a loop where it loops through every object in the array, however it doesn't loop onto the next object until the getDataInBackgroundBlock has finished loading and the current object has been added to the new array.
-(void)organizePhotos {
for (PFFile *picture in pictureArray) {
//This block loads, after the next index in the loop is called, causing the array to be out of order.
[picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
[imageArray addObject:[UIImage imageWithData:data]];
[self savePhotos];
}];
}
}
Above is my code. Is there anyway I can make the loop wait until the getDatanBackgroundWithBlock finishes loading?
The problem is, is the loop runs and causes all of the blocks to trigger, then they are all loading at the same time
That is not a problem, it is good - the reason why the call is asynchronous is it can take an arbitrarily long time to complete. If you've got multiple downloads to do then doing then concurrently can be a big win.
thus the imageArray will result in a different order to the original array of PFFiles, because if one object finishes loading before the previous object, it will be added to the array before the previous one, making it go out of order.
This is the problem and can be addressed in a number of ways.
As you are using arrays, here is a simple array based solution: first create yourself an array of the right size and fill it with nulls to indicate the image hasn't yet arrived:
(all code typed directly into answer, treat as pseudo-code and expect some errors)
NSUInteger numberOfImages = pictureArray.length;
NSMutableArray *downloadedImages = [NSMutableArray arrayWithCapacity:numberOfImages];
// You cannot set a specific element if it is past the end of an array
// so pre-fill the array with nulls
NSUInteger count = numberOfImages;
while (count-- > 0)
[downloadedImages addObject:[NSNull null]];
Now you have your pre-filled array just modify your existing loop to write the downloaded image into the correct index:
for (NSUInteger ix = 0; ix < numberOfImages; ix++)
{
PFFile *picture = pictureArray[ix];
[picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
dispatch_async(dispatch_get_main_queue(),
^{ [imageArray replaceObjectAtIndex:ix
withObject:[UIImage imageWithData:data]
});
[self savePhotos];
}];
}
The use of dispatch_async here is to ensure there are not concurrent updates to the array.
If you wish to know when all images have been downloaded you can check for that within the dispatch_async block, e.g. it can increment a counter safely as it is running on the main thread and call a method/issue a notification/invoke a block when all the images have downloaded.
You are also possibly making things harder on yourself by using arrays, and trying to keep items in different arrays related by position. Dictionaries could save you some of the hassle, for example each of your PFFile objects presumably relates to a different URL, and a URL is a perfectly valid key for a dictionary. So you could do the following:
NSMutableDictionary *imageDict = [NSMutableDictionary new];
for (PFFile *picture in pictureArray) {
[picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
dispatch_async(dispatch_get_main_queue(),
^{ [imageDict setObject:[UIImage imageWithData:data] forKey:picture.URL];
};
[self savePhotos];
}];
}
And your can locate the image for a PFFile instance by looking up its URL in the dictionary - which will return nil if the image is not yet loaded.
There are other solutions, and you might want to look into making your code more asynchronous. Whatever you do trying to call asynchronous code synchronously is not a good idea and will impact the user experience.
HTH
Ideally, you should use a synchronous method; however the behavior you are asking for can be achieved using Grand Central Dispatch.
First, create a dispatch_group_t using dispatch_group_create(). Let's call it asyncGroup.
Then, before calling the async method, call dispatch_group_enter(asyncGroup). This increments the counter of the number of calls in the group.
Then, at the end of the async block, call dispatch_group_leave(asyncGroup) to decrement the counter of the number of calls in the group.
Finally, after calling the async method, call dispatch_group_wait(asyncGroup, timeout) to pause thread execution until the group counter reaches zero. Since you increment, make the call, and then wait, the loop will only continue when the async block has been run. Just ensure the timeout is longer than the operation will take.
You can make the code synchronous:
-(void)organizePhotos {
for (PFFile *picture in pictureArray) {
[imageArray addObject:[UIImage imageWithData:[picture getData]]];
[self savePhotos];
}
}
and run it in the background:
[self performSelectorInBackground:#selector(organizePhotos) withObject:nil];
You can use GCD approach which is to use dispatch_group. So, before you start an asynchronous task, call dispatch_group_enter, and then when the asynchronous task finishes, call dispatch_group_leave, and you can then create a dispatch_group_notify which will be called when the asynchronous tasks finish. You can marry this with a completion block pattern (which is a good idea for asynchronous methods, anyway):
Here is similar Question. Perhaps it may also be helpful:
How to wait for method that has completion block (all on main thread)?
You could do that using dispatch_async way, using semaphory, groups, but for your need better practice is using block to get response and do what you need.
Doing that:
-(void)organizePhotos:(void(^)(BOOL responseStatus))status {
for (PFFile *picture in pictureArray) {
//This block loads, after the next index in the loop is called, causing the array to be out of order.
[picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
[imageArray addObject:[UIImage imageWithData:data]];
status(YES);
}];
}
}
So you next at your call you could do that:
__weak typeof(self) weakSelf = self;
[self organizePhotos:^(BOOL responseStatus) {
if(responseStatus){
[weakSelf savePhotos];
}
}];
Or if you dont wan't create extra parameter response to your method you could do like this:
-(void)organizePhotos {
__weak typeof(self) weakSelf = self;
void (^ResponseBlock)(void) = ^{
[weakSelf savePhotos];
};
for (PFFile *picture in pictureArray) {
//This block loads, after the next index in the loop is called, causing the array to be out of order.
[picture getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
[imageArray addObject:[UIImage imageWithData:data]];
ResponseBlock();
}];
}
}
And another point that you are making wrong when calling "self" inside block, this could lead you to retain cycle and its a bad practice, look at my code how you should do.
I have the following requirement:
Given a hierarchical tree-like structure, I am performing a breadth-first-search to walk through the WHOLE dataset. The data is being provided by an API with a method : (makes a request to a server using AFNetworking, saves the result to Core Data and calls back the completion block on success with the stored entries)
-(void) getChildrenForNodeId:(NSNumber*)nodeId
completion:(void (^)(NSArray *nodes))completionBlock;
The method which a controller executes to fetch data:
-(void)getAllNodesWithCompletion:(void (^)(NSArray *nodes))completionBlock{
NSNumber *rootId = ...
[MyNetworkManager getChildrenForNodeId:rootId completion:^(NSArray *nodes){
for(Node *node in nodes){
[self iterateOverNode:node.nodeId];
}
//execute completionBlock with nodes fetched from database that contain all their children until the very last leaf
}];
}
Here is the problem:
-(void)iterateOverNode:(NSNumber*)nodeId {
NSMutableArray *elements = [NSMutableArray array];
[elements addObject:nodeId];
while ([elements count]) {
NSNumber *current = [elements objectAtIndex:0];
[MyNetworkManager getChildrenForNodeWithId:current completion:^(NSArray *nodes) {
/**
In order to continue with the loop the elements array must be updated. This can only happen once we have retrieved the children of the current node.
However since this is in a loop, all of these requests will be sent off at the same time, thus unable to properly keep looping.
*/
for(Node *node in nodes){
[elements addObject:node.nodeId];
}
[elements removeObjectAtIndex:0];
}];
}
}
Basically I need the result of the callback to control the flow of the while loop but I am not sure how to achieve it. My understanding is that the request to getChildrenForNodeWithId:completion: from within the while-loop should happen in a new thread in a SERIAL order so that another should commence after the first one has completed. I am not sure how to achieve this neither with NSOperation nor with GCD. Any help will be greatly appreciated.
What you need here is some recursion. This problem is tricky as we also need a way to track and detect the point at which we have explored every branch to a leaf node.
I'm not a expert with tree search algorithms, so some folks could probably improve on my answer here. Kick this off by calling it with the root node id. self.trackingArray is an NSMutableArray property with __block qualifier. Each time we start a request for a Node, we add it's nodeId into this array, and when it returns, we remove it's nodeId, and add the nodeIds of it's children. We can then know that when count of the tracking array reaches 0, every request made has returned, and has not added child ids to the array. Once you detect we are finished, you could call a saved block or a delegate method.
This solution does not include any error handling. If any request fails, it won't be retried, and all child nodes will be ignored.
- (void)getNodesRecursively:(NSNumber *)nodeId {
// Only do this once
if (self.trackingArray == nil) {
self.trackingArray = [NSMutableArray new];
[self.trackingArray addObject:nodeId];
}
__weak typeof(self) weakSelf = self;
[MyNetworkManager getChildrenForNodeWithId:nodeId completion:^(NSArray *nodes) {
[self.trackingArray removeObject:nodeId];
for (Node *node in nodes) {
[weakSelf.trackingArray addObject:node.nodeId];
[weakSelf getNodesRecursively:node.nodeId];
}
if (weakSelf.trackingArray.count == 0) {
// We are done.
// Reset the state of the trackingArray
self.trackingArray = nil;
// call a method here to notify who ever needs to know.
[weakSelf allNodesComplete];
}
}];
}
Your other methods would look something like this
-(void)getAllNodesWithCompletion:(void (^)(NSArray *nodes))completionBlock{
// Save the block to a property
self.completion = completionBlock;
// Call -getNodesRecursively with root id
[self getNodesRecursively:rootNodeId];
}
Then you could have a second method
- (void)allNodesComplete {
// Call saved block
// Completion should load nodes from core data as needed
self.completion();
}
I haven't tested the code, but does that approach seem sensible? I'm assuming we don't need to capture the here nodes, as they can be loaded from core data as required.
I made my search algorithm for my UISearchBar, and I know that I must search in a background thread. Honestly I'm not familiar with multi-threading, so I'm looking for help. I'm using GCD (Grand Central Dispatch).
Here is my code, I want to know is it correct or not.
-(void)mySearchMethod
{
NSArray *allObjects = self.allMyObjects;
__block NSMutableArray *searchArray = [[NSMutableArray alloc] init];
dispatch_queue_t queue;
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async( queue, ^{
for (MyObjectClass *myObjectAsDictionary in allObjects) {
for (NSString *titleSubString in [myObjectAsDictionary.title componentsSeparatedByString:#" "]) {
if ([[titleSubString lowercaseString] hasPrefix:[text lowercaseString]]) {
[searchArray addObject: myObjectAsDictionary];
}
}
}
dispatch_async( dispatch_get_main_queue(), ^{
self.tableObjects = searchArray;
[self.myTableView reloadData];
});
});
}
So does this code work in the background, or is it blocking the main thread?
Can the contents of allMyObjects change while the search is running? If so, you have two problems. First problem: you should not accessing an array on one thread while it's being mutated on another. You can fix this simply by copying the array: NSArray *allObjects = [self.allMyObjects copy];. Second problem: your search results might contain objects that have been removed from allMyObjects, or might be missing objects that have been added to allMyObjects. This is a hard problem to solve and how you solve it depends on your app.
What happens if the user cancels the search before your async block finishes running? Will it replace the contents of a table with search results even though the user doesn't want to see the results now?
NSString *pictureUrl = [[[oneUserDict objectForKey:#"picture"]objectForKey:#"data"]objectForKey:#"url"];
[[AppEngine sharedEngine]imageAtURL:[NSURL URLWithString:pictureUrl] onCompletion:^(UIImage *fetchedImage, NSURL *url, BOOL isInCache)
{
int index = [usersArray indexOfObject:oneUserDict];
NSString *loadName = [NSString stringWithFormat:#"%d of %d",index,[usersArray count]];
NSLog(#"%i",usersArray.count);
int temp=[usersArray count]-10;
if (index!=temp)
{
[[LoadingIndicator currentIndicator]displayActivity:loadName];
NSLog(#"inside loading indicator");
}
else
{
[[LoadingIndicator currentIndicator]displayCompleted:#"Done"];
NSLog(#"finally done");
}
aPerson.image = UIImagePNGRepresentation(fetchedImage);
[appDelegate.managedObjectContext save:nil];
}];
AppEngine is the subclass of MKNetworkEngine which uses a method called imageAtURL:onCompletion:
what I am currently doing is retrieving all the images from a particular url and and storing them in aPerson.image,basically the above code is in a FOR loop(i.e the for the count of users).
Issues
The above code which is in the completion block never gets executed,i dont know why but i have put a breakpoint inside the block but still the compiler wont run the statements inside the completion block.
Api imageAtURL:onCompletion: is deprecated. Use imageAtURL:completionHandler:errorHandler: instead. Also MKNetworkKit provides for UIImageView+MKNetworkKitAdditions category which provides simple API for image download like setImageFromURL: placeHolderImage:
Cheers!
Amar.