Code executed out of order

Discussion in 'iOS Programming' started by spaceman816, Jun 17, 2010.

  1. spaceman816 macrumors member

    Joined:
    Jul 29, 2009
    #1
    This is the simplified version of my problem:

    .h
    Code:
    NSString *string;
    @property ....
    .m
    Code:
    @synthesize string;
    - (void)method1 {
    	//Call requester method
    	NSLog(string);
    }
    
    - (void)request:(Requester*)request didLoad:(id)result {
            string = @"changed string";
    }
    The NSLog in method1 is called before the string is able to change in the request method. The only way i know of to make sure this happens in order is to set up a delay, but that isn't ideal or consistant.
    Thanks
     
  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    When do you actually call the request:didLoad: method? Looks to me that you just have a comment stating that intention before you go right into the NSLog().
     
  3. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #3
    You're not properly using the string property that you defined, so it's possible that the value you assign to string in the request method is being lost.

    Generally speaking, anywhere you assign a value to string you want to do

    Code:
    self.string = @"whatever";
    
    to make sure that the new value is retained and the old value released.
     
  4. seepel macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #4
    To elaborate a little bit...
    Code:
    string = @"some string"
    Simply points the string pointer to the constant string in your application. This can cause problems if you point it to something else, for example an autoreleased NSString. Then you'll have no idea if that string is actually still in memory or not.

    What I usually use are retain properties like this
    Code:
    @property (nonatomic, retain) NSString *string;
    This way the
    Code:
    @synthesize string;
    turns into this
    Code:
    - (void) setString:(NSString*)s {
        [string release];
        string = s;
        [string retain];
    }
    
    And
    Code:
    self.string = @"Some string"
    translates to
    Code:
    [self setString:@"Some string"]
     
  5. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #5
    I always forget that string literals are statically allocated rather than autoreleased (although of course you should still retain and release them if necessary). I think the OP needs to post all of their real code so we can really see what is happening.

    It's not clear whether string is ever initialized before the NSLog, so it is possible by chance or some optimization that it points to @"changed string" already.
     
  6. spaceman816 thread starter macrumors member

    Joined:
    Jul 29, 2009
    #6
    I'm sorry I wasn't clear enough. I'll post more of my code.
    I am working with the facebook api- that's not so relevant so I didn't want to confuse everything. Important lines of code are bolded:

    .h:
    Code:
    NSString *lastLink;
    Code:
    @property (nonatomic, retain) NSString *lastLink;

    .m:
    Code:
    - (void)postToWall {
    	NSDictionary *params = nil;
    	[B][[FBRequest requestWithDelegate:self] call:@"facebook.photos.upload" params:params dataParam:(NSData*)drawImage.image];
    	NSLog(@"second test =%@",lastLink);[/B]
    	FBStreamDialog* dialog = [[[FBStreamDialog alloc] init] autorelease];
    	dialog.userMessagePrompt = @"Enter your message:";
    	dialog.attachment = [NSString stringWithFormat:@"{\"name\":\"lorem ipsum\",\"href\":\"http://www.sample.com/\",\"caption\":\"%@\",\"description\":\"\",\"media\":[{\"type\":\"image\",\"src\":\"%@\",\"href\":\"http://www.sample.com\"}]}",
    						 _facebookName, _facebookName, lastLink];	
    	dialog.actionLinks = @"[{\"text\":\"lorem ipsum\",\"href\":\"http://www.sample.com/\"}]";
    	[dialog show];
    }
    
    - (void)request:(FBRequest*)request didLoad:(id)result {
    	if ([request.method isEqualToString:@"facebook.photos.upload"]) {
    		NSDictionary* photoInfo = result;
    		[B]lastLink = [photoInfo objectForKey:@"src_small"];
    		NSLog(@"first test =%@",lastLink);[/B]
            }
    }
    This what I want to happen: When I call [[FBRequest requestWithDelegate:self] call:mad:"facebook.photos.upload"... the view loads and the didLoad method is called. lastLink changes to the correct link (and the NSLog for first test appears), and then when the NSLog is called for the second link, it has been changed already so I have the right string.

    This is what actually happens: Things don't happen in the correct order. The didLoad method is called, but somehow the rest of the code in the postToWall method continues to be called either simultaneously. The result is that the NSLog for second test displays before first test, and I don't have the correct value for last link until a few seconds later.

    I hope this makes sense now.
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    I'm not familiar with the FB API but it looks like your FBRequest is a wrapper for NSURLConnection. NSURLConnection works asynchronously. Meaning that you queue up a connection request and then later it calls back the various delegate methods with the results. It's not done until you get the final delegate callback.

    In the code you show this appears to be the request:didLoad: method.

    You need to change your design so that it doesn't move to the next step until the request:didLoad: method has been called (or the request:didntLoad method for errors). It would seem that just moving the code that shows the dialog to the request:didLoad: method would work.

    You're thinking that things work synchronously but they don't work that way. And on a cell phone it's better that they don't.
     
  8. spaceman816 thread starter macrumors member

    Joined:
    Jul 29, 2009
    #8
    Any tips for how to do this? The only way I can think of is with the performSelector:withObject:afterDelay: method, and it doesn't seem possible that delaying the method for a specified amount of time can be the most efficient method.
     
  9. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #9
    What's wrong with this:

    Code:
    - (void)postToWall {
    	NSDictionary *params = nil;
    	[[FBRequest requestWithDelegate:self] call:@"facebook.photos.upload" params:params dataParam:(NSData*)drawImage.image];
    	NSLog(@"second test =%@",lastLink);
    }
    
    - (void)request:(FBRequest*)request didLoad:(id)result {
    	if ([request.method isEqualToString:@"facebook.photos.upload"]) {
    		NSDictionary* photoInfo = result;
    		lastLink = [photoInfo objectForKey:@"src_small"];
    		NSLog(@"first test =%@",lastLink);
    
    	FBStreamDialog* dialog = [[[FBStreamDialog alloc] init] autorelease];
    	dialog.userMessagePrompt = @"Enter your message:";
    	dialog.attachment = [NSString stringWithFormat:@"{\"name\":\"lorem ipsum\",\"href\":\"http://www.sample.com/\",\"caption\":\"%@\",\"description\":\"\",\"media\":[{\"type\":\"image\",\"src\":\"%@\",\"href\":\"http://www.sample.com\"}]}",
    						 _facebookName, _facebookName, lastLink];	
    	dialog.actionLinks = @"[{\"text\":\"lorem ipsum\",\"href\":\"http://www.sample.com/\"}]";
    	[dialog show];        }
    }
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    As PhoneyDeveloper said:
     
  11. spaceman816 thread starter macrumors member

    Joined:
    Jul 29, 2009
    #11
    Sorry. Don't know why I missed that, or why I didn't thing of that myself.
    Thanks a lot.
     
  12. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #12
    The code hilited in red is not using the property accessor. It's doing a direct assignment to the instance variable. One consequence of this is that a retain is not being done. This will probably prove fatal later on.

    The purple code is also not using a property accessor. Here it's not as problematic.

    If you're going to go to the trouble to declaring properties, then you must use them consistently. If you're not going to do that, then you're better off not declaring every ivar as being a property.

    One self-defeating feature I see in this code is that your property name and ivar name are identical. This makes it harder to detect inappropriate ivar uses that ought to be property uses. The compiler always behaves as if you fully understand what you're doing and why. It doesn't have a "do the safe thing I should have written, not the unsafe thing I wrote" mode.

    Avoid that by using different names for property and ivar, and establishing the correlation when the property is defined.
     
  13. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #13

Share This Page