Link to home
Start Free TrialLog in
Avatar of Philippe Renaud
Philippe RenaudFlag for Canada

asked on

NSMutableArray in Objective-C

Hello EE-Mac,

I am doing an app in XCode with objective-C.

My problem is as follow:

I have a code that read a web service that receive some data (see snippet 1)

ok so here I am used to .NET Technology. when you see the line   action:@selector(handleGetPresentations:)
I thought XCode would go inside that BEFORE going to the next line wich is a NSLog then the return. But I see that it goes to the NSLog before entering the handleGetPresentation. so my finalList count is 0 and it returns nothing.

I dont understand how to return my array with an action:@selector

Is it possible maybe to instead of writing (void) in my handleGetPresentations to return (NSMustableArray*) ?
if so, I dont understand how in loadPresentations to retrive that return value.

any idea??

 
@synthesize listData;

...

-(NSMutableArray*)loadPresentations{
    NSLog(@"Test");
    SDZService* service = [[SDZService alloc]init];
    [service GetPresentations:self action@selector(handleGetPresentations:)];

    NSLog(@"count is, %i", finalList.count);
    return finalList;
}

-(void) handleGetPresentations:(id)value {
    if ([value isKindOfClass:[NSError class]]) {
        NSLog(@"This is an error: %@", value);
        return;
    }
    if ([value isKindOfClass:[SoapFault class]]) {
        NSLog(@"This is a fault: %@", value);
        return;
    }
    
    NSMutableArray* a = value;
    NSMutableArray* finalArray = [[NSMutableArray alloc] init];
    
    for (SDZPresentation* p in a){
        Presentations* pr = [[Presentations alloc] init];
        pr.presentationID = p.ID;
        pr.presentationDesc = p.Description;
        pr.presentationComments = p.Comments;

        [finalArray addObject:pr];
        [pr release];
    }
    
    self.listData = [[NSMutableArray alloc] initWithArray:finalArray];
    [finalArray release];
    
    //return listData;
    finalList = listData;
    
    NSLog(@"count is %i", finalList.count);
}

Open in new window

Avatar of SpeedyApocalypse
SpeedyApocalypse
Flag of Canada image

When you perform a selector, it will not return anything.

I would recommending housing all of the necessary code in your -(NSMutableArray*)loadPresentations: method.  There really isn't a reason to have another method that just sets a property, unless you re-use it a fair bit.
Avatar of Philippe Renaud

ASKER

ok so mmm...  not sure how to call my service GetPresentations then...
It's hard to provide assistance when custom classes are involved.  What is the SDZService class, and what does it do?  Just as well, as you allocated that class, it should be released or autoreleased before the method ends.
Actually the class came from www.sudzc.com

it creates classes for you with a given .net web service

the class is in the snippet (interface, implementation)

so its simply some SoapActions    I see that there is a  GetPresentations:  without _action to be invoke I might need to call that,  im still new to all this thats why I came here actually.



You are right about the release, I forgot.

@interface SDZService1 : SoapService
    /* Returns NSMutableArray*.  */
    - (SoapRequest*) GetPresentations: (id <SoapDelegate>) handler;
    - (SoapRequest*) GetPresentations: (id) target action: (SEL) action;

    + (SDZService1*) service;
    + (SDZService1*) serviceWithUsername: (NSString*) username andPassword: (NSString*) password;
@end



    /* Returns NSMutableArray*.  */
    - (SoapRequest*) GetPresentations: (id <SoapDelegate>) handler
    {
        return [self GetPresentations: handler action: nil];
    }

    - (SoapRequest*) GetPresentations: (id) _target action: (SEL) _action
        {
        NSMutableArray* _params = [NSMutableArray array];
        
        NSString* _envelope = [Soap createEnvelope: @"GetPresentations" forNamespace: self.namespace withParameters: _params withHeaders: self.headers];
        SoapRequest* _request = [SoapRequest create: _target action: _action service: self soapAction: @"http://www.fashionize.ca/GetPresentations" postData: _envelope deserializeTo: [[SDZArrayOfPresentation alloc] autorelease]];
        [_request send];
        return _request;
    }

Open in new window

Here's an idea.  There is a method called [self performSelectorOnMainThread... waitUntilDone:YES];

Would you be able to put your [service getPresentations...] method in a new selector, and call it whilst waitingUntilDone?

-(NSMutableArray *)... {

    [self performSelectorOnMainThread:@selector(callOtherMethod) withObject:nil waitUntilDone:YES];

}

-(void)callOtherMethod {

    SDZService* service = [[SDZService alloc]init];
    [service GetPresentations:self action@selector(handleGetPresentations:)];

}

Open in new window

Well I jsut tried, and if I do a NSLog(@"count %i", finalList.count);    wich by the way finalList a static NSMutableArray at the top, right after the #imports....

it still count : 0


but the count is 3 inside handleGetPresentations.  

[self performSelectorOnMainThread:@selector(callOtherMethod) withObject:nil waitUntilDone:YES];
the count is 0....

did I misunderstood your idea? ..
Could you find the time that the NSLogs are called?  I would like to see if it waits to run the other selector and then moves on (because it should...).  I am thinking that it is an issue with setting the variables instead of method call times.

Have you considered using Properties to handle the finalList array?
I ran a test myself, with this code:

-(void)test1 {
    
    NSLog(@"This is the Test1 method.");
    
    [self performSelectorOnMainThread:@selector(test2) withObject:nil waitUntilDone:YES];
    
    NSLog(@"This is the Test1 method end.");
    
}

-(void)test2 {
    
    NSLog(@"This is the Test2 method.");
    
}

Open in new window


This is what the log showed:

2011-08-22 12:10:48.944 Test[20046:10d03] This is the Test1 method.
2011-08-22 12:10:48.955 Test[20046:10d03] This is the Test2 method.
2011-08-22 12:10:48.956 Test[20046:10d03] This is the Test1 method end.

Open in new window


So, we have established that it waits to finish the method before moving on.  Therefore, I would think that it is an issue with the way that you receive finalList.

In your .h file, do you have a property for it?

@property (nonatomic, retain) NSMutableArray *finalList;

Open in new window

@synthesize finalList;

Open in new window


(in dealloc)

[finalList release], finalList = nil;

Open in new window


Then, to set finalList, do this:

[self setFinalList:listData];

Open in new window


Then, to retrieve it, do this:

NSLog(@"%@",[self finalList]);

Open in new window

my NSLog right after the line
[self performSelectorOnMainThread:@selector(callOtherMethod) withObject:nil waitUntilDone:YES];

is : 2011-08-22 13:10:40:538           just after waitUntilDone Count: 0

then I have:

      2011-08-22 13:10:40:565           inside handleGetPresentations count:   3



for the array, I have already a  @synthesize  listData;
in the interface its a property nonatomic, retain  NSMustableArray*

Actually I put a static finalList because I was going empty on ideas for that problem.

I guess I could just instead of doing [finalList addObject:pr];   I could take the listData and do  [listData adDObject:pr];   ?
Wait let me see something I might have did an error with your code, by right back
Ok so now I have this:


result is..:

2011-08-22 13:20:44.138 Fashionize[747:207] Test2
2011-08-22 13:20:44.140 Fashionize[747:207] jsut after waitUntilDone 0
2011-08-22 13:20:44.169 Fashionize[747:207] inside handleGetPresentations 0
@implementation PresentationLoader
@synthesize listData;

-(NSMutableArray*)loadPresentation{    
    [self performSelectorOnMainThread:@selector(handleSelectorGetPresentations) withObject:nil waitUntilDone:YES];
    
    NSLog(@"just after waitUntilDone %i", finalList.count);
    return finalList;
}

-(void) handleSelectorGetPresentations{
    NSLog(@"Test2");
    SDZService1* service = [[[SDZService1 alloc]init] autorelease];
    [service GetPresentations:self action:@selector(handleGetPresentations:)];  
}

-(void) handleGetPresentations:(id)value {
    if ([value isKindOfClass:[NSError class]]) {
        NSLog(@"This is an error: %@", value);
        return;
    }
    if ([value isKindOfClass:[SoapFault class]]) {
        NSLog(@"This is a fault: %@", value);
        return;
    }
    
    NSMutableArray* a = value;
    NSMutableArray* finalArray = [[NSMutableArray alloc] init];
    
    for (SDZPresentation* p in a){
        Presentations* pr = [[Presentations alloc] init];
        pr.presentationID = p.ID;
        pr.presentationDesc = p.Description;
        pr.presentationComments = p.Comments;

        [listData addObject:pr];
        [pr release];
    }
    
    self.listData = [[NSMutableArray alloc] initWithArray:finalArray];
    [finalArray release];
    
    //return listData;
    //finalList = listData;
    
    NSLog(@"inside handleGetPresentations %i", finalList.count);
}

-(void) dealloc{
    [listData release];
    [super dealloc];
}

@end

Open in new window

sorry I wrote finalList but even If I replace with listData  it still 0...


Damn Im lost now.
Basically, what I see happening is that you call the [service GetPresentations:...], and then that method ends.  Do you know if [service getPresentations:...] calls anything on a new thread?  Have you thought about implementing delegation to let the caller know when you are done downloading the data?

What you could do is call the loadPresentation method, then have that call the SDZService.  From there, you could have it send out a message to the delegates when handleGetPresentations finishes.
I dont think it goes on new threads.

For the rest of your comment... Not sure to understand.

If I recap..  I already call loadPresentation.  thats fine for now.
If I call the SDZService with the action:@selector yes it will load my property Array inside it (because the NSLog inside count is 3) ..

what do you mean by sending a message to the delegates
Basically, there will be no way to do that you are setting up as when it downloads, it will be multithreading as to not slow down your application.  What I am suggesting is to implement delegation, so when downloading finishes, you can notify the class that called the download and give the relevant information.
Alright I see.

So finally, what should I type in google to have some example of that, delegation ?     is there a reference name for when you say : "when download finishes you do that and that.."  in other words, how can I know it finishes?
ASKER CERTIFIED SOLUTION
Avatar of SpeedyApocalypse
SpeedyApocalypse
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thanks