Restart a non-void function - iphone

I have the following code:
- (NSString*)returnIncorrectDef {
NSInteger defIndex = [self randomIndex];
NSString *incorrectDef = [NSString stringWithFormat:#"%#", [definitions objectAtIndex:defIndex]];
if (([incorrectDef isEqualToString:self.definitionString]) || ([incorrectDef isEqualToString:def1.titleLabel.text])) {
// I want to restart the method here
[self returnIncorrectDef];
} else {
return incorrectDef;
}}
I want to be able to restart this method until the if clause is not met. However when I try this code I get an error saying: "Control may reach end of non-void function". How would I do this?

Use a while loop with a flag:
- (NSString*)returnIncorrectDef {
BOOL done = NO;
NSString *incorrectDef = nil;
while (!done) {
NSInteger defIndex = [self randomIndex];
incorrectDef = [definitions objectAtIndex:defIndex];
done = !([incorrectDef isEqualToString:self.definitionString] ||
[incorrectDef isEqualToString:def1.titleLabel.text]);
}
return incorrectDef;
}
You can also use do ... while statement.
Note that [NSString stringWithFormat:#"%#",...] is a pointless statement and has been simplified. (see #medvedNick's comment - it's not pointless if that class isn't an NSString class).
IMPORTANT: Without the while loop, you are using recursion, which could potentially crash your program if you keep "missing" your target (which is certainly possible).

trojanfoe's solution is better, but the issue you have is that you never return the value that you get when you reenter. CHanging that line to
return [self returnIncorrectDef];
should fix the problem

Instead of [self returnIncorrectDef]; simply do:
return [self returnIncorrectDef];
That way you return a value by recursively calling your function. Be sure that the evaluation parameters change though, to prevent an infinite loop
EDIT but this might not be what you want in this particular case (calling the method over and over again)

Related

How to call a non-void function? Xcode

How do I call a non-void function? Normal [self methodName]; works. But how do I do this for a method that returns an NSString. I keep getting an error. For example:
+ (NSString *)formulateYQLRequestFor:(NSArray *)tickers
How do I call this? [self formulateYQLRequestFor]; gives me an error.
Sorry about the formatting, for some reason safari won't let me indent.
Thanks!
+ designates a class function. You call it with the class name, not an instance.
Instead of:
[self formulateYQLRequestFor:myArray];
Do this:
[MyClassName formulateYQLRequestFor:myArray];
Alternatively, you can do this:
[[self class] formulateYQLRequestFor:myArray];
You don't have to do anything with the return value if you don't want to. At least with ARC, the return value will be automatically released. However, since it's unlikely that the function does anything on its own, you probably should do something with the return value:
NSString *returnValue = [[self class] formulateYQLRequestFor:myArray];
// Do something with returnValue
Finally, if you want to call the function without passing in an array, you still need the array parameter, but perhaps the function will accept nil for the array:
NSString *returnValue = [[self class] formulateYQLRequestFor:nil];
There are two problems with your call to [self formulateYQLRequestFor];
Firstly, the method takes a parameter, which you haven't provided. Because of this, the compiler is looking for the method called formulateYQLRequestFor instead of formulateYQLRequestFor: This is significant, because the : is part of the method name in Objective-C. So you are trying to call a method that doesn't exist.
Secondly, self is sending a message to an instance of your class. The + in the method signature indicates that you have a class method, and so self does not respond to the method you are trying to call.
The correct way to do this is:
NSString *resultString = [[self class] formulateYQLRequestFor:someArray];
where someArray is a valid NSArray parameter.
I don't know what - (NSString *)formulateYQLRequestFor: does with the NSArray, but if it isn't necessary you can just call [self formulateYQLRequestFor:nil];. Alternatively you can call it with an empty array [self formulateYQLRequestFor:[NSArray array]];.

Recursive call within a block completion in animateWithDuration?

I have a set of animations that need to operate sequentially with various checking done at each step. Because the size of the set is determined at run time, I expected to utilize a recursive call... but I am having trouble getting it to function within the 'block' paradigm.
The result is an EXEC_BAD_ACCESS regardless of whether I predeclared the block using
__block void (^myBlock)(BOOL) = ^(BOOL finished){ if (finished) [self nextStep];};
or not, as seen in the following code snippet. Upon debugging, it appears that the 'self' variable is indeed valid.
NSEnumerator* stepEnumerator;
-(void) mainRoutine {
stepEnumerator = [myArray objectEnumerator];
[self nextStep];
}
-(void) nextStep {
id obj;
if ((obj = [stepEnumerator nextObject])) {
// Do my checking at each location
....
// we have another spot to move to
[UIView animateWithDuration:animationDuration
animations:^{self.frame = newFrame;}
completion:^(BOOL finished){ if (finished) [self nextStep];}];
}
}
else {
// we are done, finish house cleaning
}
}
Any help is greatly appreciated.
Thanks!
The answer to the question posed is, yes, recursive calls within a block completion are valid.
#BillBrasky brought up a good point about the block losing scope. I do not know enough to say if this is required or not as I have not found it to be an issue with my situation. Everything appears to work correctly for me on each successive iteration through my recursive function.
The core issue with the code as I originally wrote it and submitted it is the use of the FastEnumerator. This is DEFINITELY lost when you leave the current function and venture out into another event loop / new section of the stack frame. I realized as I thought more about it that there is probably quite a bit going on behind the scenes to make FastEnumeration work and it is quite logical that leaving the method would destroy the setup.
As a fix, I replaced the NSEnumerator with a simple integer that I then increment each time through the recursive function. I am not a big fan of this as it could lead to Out of Bounds style issues where as the FastEnumerator will not, nor will for (obj in array), but I don't know of another solution. I think I will post that as a separate question...
Corrected code:
int index;
-(void) mainRoutine {
index = 0;
if (index < [myArray count]) {
[self nextStep];
}
}
-(void) nextStep {
// obtain the object from the array
id obj = [myArray objectAtIndex:index++];
// do my checking on the object
...
[UIView animationWithDuration:animationDuration
animations:^{self.frame = [view frame];}
completions:^(BOOL finished) {
if (finished && index < [myArray count]) {
[self nextStep];
}
else {
// We are done, clean up
...
}
}];
}
Thanks again #BillBrasky, you helped point me down the correct path to resolve this. I was too focused on the recursion and my quick analysis of my 'self' object looked fine because everything except for one item was fine. Couple that with the debugger breaking on the block, not the actual offending line and who knows how long I would have been staring at the code without seeing the real issue.
Cheers.
I'm new to blocks too, but I just got done with a similar issue. In my case, the EXEC_BAD_ACCESS was caused because the block had gone out of scope. I suspect that sometime in your 2nd recursion, the block gets created with an odd stack frame because it's executing inside another block.
My solution was to keep the blocks in a property marked copy, e.g.
#property (nonatomic,copy) BOOL (^callback)(DownloadProgress*);
This ensures that everything is retained in a copy in case the original block object goes out of scope and is GC'd.

Perform Method With int Return Value in Background Thread

I'm trying to speed up my application performance by performing calculations in background threads but I'm having trouble doing this. Originally I had been using
[self performSelectorInBackground:#selector(calculateValue:) withObject:[words objectAtIndex:row]];
which was fine when my selector was a void method. However, I'm trying to do something similar to but obviously the below code isn't valid.
int value = [self performSelectorInBackground:#selector(calculateValue:) withObject:[words objectAtIndex:row]];
Any help is greatly appreciated.
Updated
Here is the route I'm currently going. I don't know how to call back to the main thread to send the updated value from computeWordValue to my cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
int value = [self performSelectorInBackground:#selector(calculateWordValue:) withObject:[wordsSection objectAtIndex:row]];
NSString *pointValue = [[NSString alloc] initWithFormat:#"Point:%d",value];
cell.pointLabel.text = pointValue;
}
-(void)calculateWordValue:(NSString *)word {
[self performSelectorOnMainThread:#selector(computeWordValue:) withObject:word waitUntilDone:YES];
}
-(int)computeWordValue:(NSString *)word {
return totalValue; //This will be a randomly generated number
}
Heres a way I use to do it:
-(void) calculateValue:(id) obj
{
// calculate value
[self performSelectorOnMainThread:#selector(didFinishCalculating:) withObject:[NSNumber numberWithInt:value]];
}
-(void) didFinishCalculating:(NSNumber *) val
{
// do what you need to do here
}
This really doesn't solve your problem I don't think, but it should at least give you a starting point.
UPDATE:
Your new code shows me that you don't really need to perform this in the background, just cache the value using an NSDictionary or something. Here's an example:
-(int) calculateValue:(id) obj
{
if ([valuesCache objectForKey:obj] == nil)
{
// calculate value
[valuesCache setObject:[NSNumber numberWithInt:result] forKey:obj];
return result;
}
else
{
return [[valuesCache objectForKey:obj] intValue];
}
}
There's no way -performSelectorInBackground: ... could return the value of the method you're calling because it actually returns before the selector was even executed. That selector will be executed on a background thread asap.
The solution is handling the result of your method asynchronously, as Richard pointed out (the method in his answer should be - (void)didFinishCalculating:(NSNumber*)val, because only objects can be passed in -performSelector: ... calls):
Perform your selector on a background thread
Call your result handler method on the main thread. You should do that on the main thread in just about any case because some things in Mac OS X and iOS are designed to just run on the main thread, like GUI updates.

NSString function

I get a null return when i try out my NSString function.
//Track.m
static NSString* trackUrl;
//static NSString* getTrackNumberUrl;
#implementation Track
- (NSString*)trackUrl {
return #"http://site.com/?a=";
}
- (NSString*)setTrackNumberUrl:(NSString*)trackNumberUrl {
if (trackUrl != trackNumberUrl) {
return [trackUrl stringByAppendingFormat:trackNumberUrl];
}
return #"Error no trackNumber";
}
- (NSString*)getTrackNumberUrl:(NSString*)trackNumber {
return [[[self alloc] setTrackNumberUrl:trackNumber] autorelease];
}
#end
MainView.m, just to show the return answer in NSlog
- (NSString *) trackNumber{
return [track getTrackNumberUrl:#"86147224549XX"];
}
- (void)drawRect:(CGRect)rect {
NSLog(trackNumber);
}
I get a null return answer? Have i miss something? Thanks.
Edit some in Track.m
- (NSString*)setTrackNumberUrl:(NSString*)trackNumberUrl {
if (trackUrl != trackNumberUrl) {
return [trackUrl stringByAppendingString:trackNumberUrl];
}
return #"Error no trackNumber";
}
- (NSString*)getTrackNumberUrl:(NSString*)trackNumber {
return [[[Track alloc] setTrackNumberUrl:trackNumber] init];
}
This is how it should work.
getTrackNumberUrl --> setTrackNumberUrl --> trackUrl (return) --> setTrackNumberUrl + trackNumber --> getTrackNumberUrl (trackNumberUrl = trackUrl + trackNumber)
I have this code to set reference to Track
#class Track;
#interface MainView : UIView {
Track *track;
}
#property (nonatomic, retain) IBOutlet Track *track;
Well if don't should use self alloc, what should i use?
You have a lot of problems with your code.
return [trackUrl stringByAppendingFormat:trackNumberUrl];
You should not use an arbitrary string as a format, because if it contains a format specifier like "%d" then the method will go looking for a variable that isn't there, and will likely crash. You should use stringByAppendingString: instead. However, that doesn't seem to be what you want here, since the method name is setTrackNumberUrl:. If you want to change the value of the trackUrl variable, you can't call stringByAppendingFormat:; all that does is return a new string and leave the original alone. I think you simply want something like
[trackUrl release];
trackUrl = [trackNumberUrl retain];
Another problem:
return [[[self alloc] setTrackNumberUrl:trackNumber] autorelease];
In this context, self is an instance of Track. An instance won't understand the alloc message, that must be sent to a class. It will return a new instance, to which you should send an init message. So you would do something like [[Track alloc] init].
NSLog(trackNumber);
The first parameter to NSLog is a format string, so for the same reasons as above you shouldn't use a variable, you should do something like this: NSLog(#"%#", trackNumber); That line of code prints the value of the variable, trackNumber. Considering that you have a method named trackNumber just above it, I wonder if what you really want to do is call the method and get the result. In that case, you need to write it as [self trackNumber] which will call the method and return an NSString.
Most probably track is nil in the trackNumber - have you set it to a correct reference to a Track object?
Also, this code
- (NSString*)getTrackNumberUrl:(NSString*)trackNumber {
return [[[self alloc] setTrackNumberUrl:trackNumber] autorelease];
}
is incorrect. Why are you using [self alloc]? You're allocating a new Track object (using a static method on an object reference, not on a class name, which is an error), setting it's track number URL, and returning an autoreleased NSString, but you're leaking the Track object you allocated.
return [trackUrl stringByAppendingFormat:trackNumberUrl];
I'm not sure bout this one,
try using it as a format for string.
return [trackUrl stringByAppendingFormat:#"%#",trackNumberUrl];

Returning multiple values from a method in Objective-C

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 *test2;
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) {
...
...