开发者

Returning multiple values from a method in Objective-C

开发者 https://www.devze.com 2022-12-10 06:50 出处:网络
I asked a similar question, but I couldn\'t get it working exactly. I\'m building an iPhone app, and there is a method that I want called from different files. I figured the easiest way would simply b

I asked a similar question, but I couldn't get it working exactly. I'm building an iPhone app, and there is a method that I want called from different files. I figured the easiest way would simply be to make a method in another file, and call the method from the other files.

Here are some problems. I need to return multiple values from the method, after passing it multiple values. For example, I'm passing it: (int, int, int, string, string). And it needs to return all of those values, after they have been changed. Someone showed me this code:

- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness
{
    varTurns--;

    if (varTurns <= 0) {
        varFatness = varFatness - 5;
    }
    else {
        varFatness += 2;
    }

    return [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:varFatness], @"FATNESS", [NSNumber numberWithInt:varTurns], @"TURNS", nil];

}

However, this code doesn't work, and I need some more information to really understand it. Let's assuming I'm passing it these values:

int varMoney;
int varNumSheep;
int varNumShepherds;
NSString *test1;
NSString *tes开发者_开发问答t2;

So I need to get all of these values back from the method.

How do I declare this in the header file? This should be in an Objective-C file, but could you give me the code for the entire file so I can see where it would go with the @implementation and @end, whatnot. Also, how would I call this method?


What about passing in the values as pointers?

For example:

- (void) getValuesForInt:(int *)int1 anotherInt:(int *)int2 aBool:(BOOL *)bool1 anotherBool:(BOOL *)bool2 {
  if (*int1 == 42 && *int2 == 0) {
    *int1 = 0;
    *int2 = 42;
  }
  if (*bool1 == NO) {
    *bool2 = YES;
  }
}

Then you can invoke it like:

int int1 = 42;
int int2 = 0;
BOOL bool1 = NO;
BOOL bool2 = NO;
[self getValuesForInt:&int1 anotherInt:&int2 aBool:&bool1 anotherBool:&bool2];
NSLog(@"int1: %d int2: %d bool1: %d bool2: %d", int1, int2, bool1, bool2);
//prints "int1: 0 int2: 42 bool1: 0 bool2: 1"

Edit:

This works equally well with objects. You'll often see this used when dealing with NSError objects:

NSError *error = nil;
[anObject doSomething:foo error:&error];

Can be implemented as:

- (void) doSomething:(id)terrible error:(NSError **)error {
  if ([terrible isEqual:reallyBad]) {
    if (error != nil) { *error = [NSError errorWithDomain:@"domain" code:42 userInfo:nil]; }
  }
}


You can use a block closure to pass back multiple values from a method like this. -rrh

[self heyFunctionGiveMeBackTwoValuesFromThisFruitArray:@[@"apple", @"orange", @"banana", @"apple"] findThisFruit:@"apple" closureFunction:^(int fruitCount, NSString* fruitString)
{
    NSLog(@"Two values returned, int-fruitCount:%d, NSString-fruiteString:%@", fruitCount, fruitString);
}];

- (void)heyFunctionGiveMeBackTwoValuesFromThisFruitArray:(NSArray*)fruitsArray findThisFruit:(NSString*)findThisFruit closureFunction:(void (^)(int fruitCount, NSString *fruitString))passBackResultsUsingThisClosure
{
    NSInteger fruitsFound = 0;
    NSString* fruitsMessage = [NSString stringWithFormat:@"No %@ Found", findThisFruit];
    for (NSString* string in fruitsArray)
    {
        if ([string compare:findThisFruit] == NSOrderedSame)
        {
            fruitsFound++;
        }
    }
    if (fruitsFound > 0)
    {
        fruitsMessage = [NSString stringWithFormat:@"You have %@ on your list this many times:%d", findThisFruit, fruitsFound];
    }
    passBackResultsUsingThisClosure(fruitsFound, fruitsMessage);
}

Results: Two values returned, int-fruitCount:2, NSString-fruiteString:You have apple on your list this many times:2


If you have that many different things that need to be returned from a method, either encapsulate it into an NSDictionary as others have suggested or consider just defining a class. You can declare the instance variables and properties to encapsulate the data, as needed.

Defining a class to encapsulate such information proves to be quite efficient and maximizes flexibility. If you need to refactor your app such that the collection of data gains new fields, needs to be saved for later, or might need to gain functionality, a class will ease these changes.


Since you can only return a single value from any method in C and C-derived languages, you simply need to return a single value that represents all of your other values. This is what your sample code is doing with an NSDictionary.

The sample code is correct, even if it's a bit contrary to common Objective-C style.

What you declare in the header file is simply the declaration of the method, that is:

@interface MyClass : NSObject
- (NSDictionary *)EndOfTurn:(int)varTurns withFatness:(int)varFatness;
@end

In the source file, then:

@implementation MyClass
// code, as given above
@end


If you only need to return primitive values, then returning a struct may be the optimal solution. You get compile-time error checking (e.g. as opposed to an NSDictionary where you could attempt to read an invalid key), while not requiring all the code/files involved in creating a class.

typedef struct myStruct {
  int varMoney;
  int varNumSheep;
  int varNumShepherds;
} myStruct;

Apple uses structs in many of their methods too (e.g. CGPoint, CGRect).

The reason this won't work with objects is because ARC forbids this.


One slight improvement to the last point in some designs is to use a struct holding enum members. This gives you the compile-time checking already mentioned, something that looks like an object in the return value, and the benefit of clear cases if you need to check the values in the return.

The struct:

typedef struct _SIXRecorderStateChange {
    SIXRecorderState oldState;
    SIXRecorderState newState;
} SIXRecorderStateChange;

The client code:

    SIXRecorderStateChange stateChange = [recorderState stop];
    if (stateChange.newState == SIXRecorderStopped) {
...
...
0

精彩评论

暂无评论...
验证码 换一张
取 消