Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 02-05-2011, 06:34 PM   #1 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default NSMutableDictionary remains empty after using nsoperation to load images

So I first coded all my methods in a viewcontroller with an NSOperationQueue. After doing some research and a lot of reading I realized i had to subclass my loadImage operation so that I may use isCancelled and cancelAllOperations. So I went ahead and created an nsoperation class and called it from my viewcontroller. ALl the methods are called, even the imageLoaded, but the NSMutableDictionary remains empty. I use the dictionary to populate my tableviewcells using the url as the Key. Also be aware that the operation call in the viewcontroller is within a method which is called by an NSInvocationOperation when the view loads.

Code:
    @interface loadImages : NSOperation {
    
        NSURL *targetURL;
    }
    
    @property(retain) NSURL *targetURL;
    
    - (id)initWithURL:(NSURL*)url;
    
    @end
implementation of nsoperation class which includes some other calls to resize the image

Code:
    @implementation loadImages
    
    @synthesize targetURL;
    
    - (id)initWithURL:(NSURL*)url
    {
        if (![super init]) return nil;
        [self setTargetURL:url];
        return self;
    }
    
    - (void)dealloc {
        [targetURL release], targetURL = nil;
        [super dealloc];
    }
    
    - (void)main {
    	
    	NSLog(@"loadImages.m reached");
    	
    	StoriesTableViewController *stories = [[StoriesTableViewController alloc] init];
    
    	NSMutableDictionary *tempDict = stories.filteredImagesDict;
       
    	UIImage *myImage = [[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[self targetURL]]]autorelease];	
    	
    	UIImage *scaledImage = [[[UIImage alloc] init] autorelease];
    	
    	
    	CGRect frame = CGRectMake(100.0f, 100.0f, 180.0f, 180.0f);
    	UIImageView *myImageFrame = [[UIImageView alloc] initWithFrame:frame];
    	
    	myImage = [[myImage croppedImage:[myImageFrame bounds]]retain];
    	
    	
    	scaledImage = [[myImage resizedImage:CGSizeMake(120.0f, 120.0f) interpolationQuality:kCGInterpolationHigh]retain];
    	
    	[tempDict setValue:scaledImage forKey:[self targetURL]];
    	
    	
        [stories performSelectorOnMainThread:@selector(imageLoaded:)
    							  withObject:myImage
                                            waitUntilDone:YES];
    	
    	NSLog(@"targetURL %@",[self targetURL]);
    	NSLog(@"tempDict count: %d",tempDict.count);
    	
    	[stories release];
    	[myImage release];
    	[myImageFrame release];
    	[scaledImage release];
    }
creation of nsoperation on viewcontroller

Code:
    for(int i=0;i<storyQuantity;i++) {
    		NSString *imageString = [[[storiesArray objectAtIndex:i] objectForKey: @"image"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];  // must add trimming to remove characters
    		 
    		NSURL *url = [NSURL URLWithString:imageString];
            loadImages *imageOperation = [[loadImages alloc] initWithURL:url];
            [queue_ addOperation:imageOperation];
            [imageOperation release];
    		
    	}
zambono is offline   Reply With Quote
Old 02-05-2011, 07:35 PM   #2 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default

You have a couple of problems here -

Code:
StoriesTableViewController *stories = [[StoriesTableViewController alloc] init];
You're creating a new StoriesTableViewController for each operation; that's probably not what you want. You need a singleton there (a thread safe one!) or you need to pass in the StoriesTableViewController when you create the NSOperation.

Otherwise I'm not sure why tempDict / filteredImagesDict is empty; did you try to log tempDict / filteredImagesDict to make sure it's not nil?

You're also adding items to the dictionary from multiple threads with no synchronization. NSMutableDictionary is not thread safe, so you could easily crash or lose data here. You need to synchronize your access:
Code:
@synchonized(tempDict){

     [tempDict setValue:scaledImage forKey:[self targetURL]];

}
Lastly, you're using UIKit classes on a background thread. You should probably switch back to the main thread after you get your data; the docs say UIKit is not thread safe.

Multithreading is hard, and IMO should be avoided unless you have no other choice. Cocoa has some great features to make it easier, but it's still tricky and not something to be entered into lightly.
__________________

Free Games!

Last edited by smasher; 02-06-2011 at 10:18 AM.
smasher is offline   Reply With Quote
Old 02-06-2011, 08:08 AM   #3 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default

ThAnks fir your response. I log the count of filteredimagesdict after every operation a d it remains 0. Is there an easier way of calling iscancelled within an nsinvocationoperation without subclassing? If not how can I pass multiple arguments to my nsoperation. I tried and the current init had me confused.

Quote:
Originally Posted by smasher View Post
You have a couple of problems here -

Code:
StoriesTableViewController *stories = [[StoriesTableViewController alloc] init];
You're creating a new StoriesTableViewController for each operation; that's probably not what you want. You need a singleton there (a thread safe one!) or you need to pass in the StoriesTableViewController when you create the NSOperation.

Otherwise I'm not sure why tempDict / filteredImagesDict is empty; did you try to log tempDict / filteredImagesDict to make sure it's not nil?

You're also adding items to the dictionary from multiple threads with no synchronization. NSMutableDictionary is not thread safe, so you could easily crash or lose data here. You need to synchronize your access:
Code:
@synchonized(tempDict){

     [tempDict setValue:scaledImage forKey:[self targetURL]];

}
Lastly, you're using UIKit classes on a background thread. You should probably switch back to the main thread after you get your data; the docs say UIKit is not thread safe.

Multithreading is hard, and IMO should be avoided unless you have no other choice. Cocos has some great features to make it easier, but it's still tricky and not something to be entered into lightly.
zambono is offline   Reply With Quote
Old 02-06-2011, 08:50 AM   #4 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

Where do you create the dictionary?
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 02-06-2011, 12:13 PM   #5 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default

The dictionary is created in the viewcontroller. Now if I could pass 2 variables in and send 2 variables out in the imageloaded method it would probably fix some issues. I would have to send out the URL and the image and the. I could add the image to he dictionary in the controller
zambono is offline   Reply With Quote
Old 02-06-2011, 12:15 PM   #6 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

Show where the dictionary is created.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 02-07-2011, 08:44 AM   #7 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default

here you go but i doubt this is the issue. the dictionary is working just fine when I use only the NSInvocationOperation and don't have a separate NSOperation to load the image.

this is within the view controller, it includes the creation to the NSOperation below.

Code:
@synthesize filteredImagesDict;

- (void)loadImages {
	
	if (filteredImagesDict) {
		[filteredImagesDict release];
		filteredImagesDict = nil;
	}
	
	filteredImagesDict = [[NSMutableDictionary alloc] initWithCapacity:0];
	[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
	NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
	int storyQuantity = [userDefaults integerForKey:kStoryQuantity];
	
	for(int i=0;i<storyQuantity;i++) {
		NSString *imageString = [[[storiesArray objectAtIndex:i] objectForKey: @"image"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];  // must add trimming to remove characters
		 
		NSURL *url = [NSURL URLWithString:imageString];
        loadImages *imageOperation = [[loadImages alloc] initWithURL:url];
        [queue_ addOperation:imageOperation];
        [imageOperation release];
zambono is offline   Reply With Quote
Old 02-07-2011, 08:55 AM   #8 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

When you log the count of a collection and get 0, there are 2 possibilities:

1. The collection doesn't actually exist
2. The collection has no contents.

To answer #1, you should log the collection itself, as smasher suggested in his first reply. That will determine whether you need to worry about #2.

You alloc/init a new StoriesTableViewController and then immediately access the filteredImagesDict. However, you don't actually create this dictionary until loadImages is called. So, unless your init method is calling this loadImages method, the dictionary doesn't exist yet.

As a side note, it is really weird to set up a mutable collection with a capacity of zero. What's the point of that? And you really should be using properties.

Edit:

One other thing I just noticed:

Code:
NSLog(@"tempDict count: %d",tempDict.count);
count is not a property, so you can't use dot syntax for this. You have to use bracket syntax.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Old 02-07-2011, 09:51 AM   #9 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default

thank you for all your pointers. I have logged each step. I get an address for myImage, a different address for scaledImage, and then I log the count of the dictionary.

loadImages is called upon initiation, and when you re-enter the view if a certain amount of time has passed.

I tried another direction which was passing a dictionary to the imagesLoaded method with the URL and the image to then add it to filteredImagesDict in the viewcontroller. Now when I NSLog the uiimages i get addresses but how can I know if there is something actually there. is there a way of logging image memory size. After my workaround, again i was getting no image to show up, and an empty filteredImagesDict in the cellForRow method

Quote:
Originally Posted by BrianSlick View Post
When you log the count of a collection and get 0, there are 2 possibilities:

1. The collection doesn't actually exist
2. The collection has no contents.

To answer #1, you should log the collection itself, as smasher suggested in his first reply. That will determine whether you need to worry about #2.

You alloc/init a new StoriesTableViewController and then immediately access the filteredImagesDict. However, you don't actually create this dictionary until loadImages is called. So, unless your init method is calling this loadImages method, the dictionary doesn't exist yet.

As a side note, it is really weird to set up a mutable collection with a capacity of zero. What's the point of that? And you really should be using properties.

Edit:

One other thing I just noticed:

Code:
NSLog(@"tempDict count: %d",tempDict.count);
count is not a property, so you can't use dot syntax for this. You have to use bracket syntax.

Last edited by zambono; 02-07-2011 at 09:57 AM.
zambono is offline   Reply With Quote
Old 02-11-2011, 08:49 AM   #10 (permalink)
Registered Member
 
Join Date: Dec 2010
Location: VA
Posts: 50
zambono is on a distinguished road
Default

this is still unresolved for me. I went ahead and put the imageload method back in the viewcontroller. could it be that i was calling an nsoperation which was allready in a method in an nsinvocationoperation? i could try to run it on the main thread.
zambono is offline   Reply With Quote
Old 02-11-2011, 09:26 AM   #11 (permalink)
Emphasizing Fundamentals
 
BrianSlick's Avatar
 
Join Date: Jul 2009
Location: NoVA / DC Area
Age: 36
Posts: 7,990
BrianSlick has a spectacular aura about
Default

Have you done this?

Code:
NSMutableDictionary *tempDict = stories.filteredImagesDict;
NSLog(@"tempDict is: %@", [tempDict description]);
You seem to be focused on the threading, and while I can't rule that out, you could be overlooking something far more basic.
__________________
BriTer Ideas LLC - Professional iOS App Development. Available for hire.

SlickShopper 2 | Free NSLog utility | Leave a PayPal donation.

Are you a newbie? Things you should read:
Definitive Guide To Properties | UITableView Series | Guide To Troubleshooting | Model Object Overview

Do you sit at a desk all day? Walk instead! Follow along with my treadmill desk adventures.
BrianSlick is offline   Reply With Quote
Reply

Bookmarks

Tags
iscancelled, nsmutabledictionary, nsoperation, uiimage

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 367
14 members and 353 guests
7twenty7, blasterbr, Clouds, dre, EvilElf, jeroenkeij, jimmyon122, Mah6447, Morrisone, n00b, pungs, Sami Gh, stanny, toon4413
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,667
Threads: 94,121
Posts: 402,900
Top Poster: BrianSlick (7,990)
Welcome to our newest member, host number one
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 03:47 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0