Explain recursive call possibly related to scope - ios

I kind of understand recursion. We start on one method and it calls itself until it reaches its base case. Please help me understand how this code works. I know we are popping off an object off stack each time it is called we are returning a double at the end when the base case is reached.Is stack being modified each time it is called? For example: 3 (Enter) 5 (Enter) * would obviously equal 15. However based on the method when I look at it I follow that it goes into multiply if statement sends stack (3,5) returns 5 then on the second call sends(3,5) and returns 5 again? Why is the second one being sent stack(3) instead of (3,5)?
+ (double)popOperandOffProgramStack:(NSMutableArray *)stack
{
double result = 0;
id topOfStack = [stack lastObject];
if (topOfStack) [stack removeLastObject];
if ([topOfStack isKindOfClass:[NSNumber class]])
{
result = [topOfStack doubleValue];
}
else if ([topOfStack isKindOfClass:[NSString class]])
{
NSString *operation = topOfStack;
if ([operation isEqualToString:#"+"]) {
result = [self popOperandOffProgramStack:stack] +
[self popOperandOffProgramStack:stack];
} else if ([#"*" isEqualToString:operation]) {
result = [self popOperandOffProgramStack:stack] *
[self popOperandOffProgramStack:stack];
} else if ([operation isEqualToString:#"-"]) {
double subtrahend = [self popOperandOffProgramStack:stack];
result = [self popOperandOffProgramStack:stack] - subtrahend;
} else if ([operation isEqualToString:#"/"]) {
double divisor = [self popOperandOffProgramStack:stack];
if (divisor) result = [self popOperandOffProgramStack:stack] / divisor;
}
}
return result;
}

You write:
I know we are popping off an object off stack each time it is called we are returning a double at the end when the base case is reached.Is stack being modified each time it is called?
So you know an object is popped off the stack, which means the stack changes. So why would you think the stack as not modified?
Are you maybe confusing the contents of the variable stack, with the object it references? The contents of the variable stack do not change - that contents is a reference to an object (or type NSMutableArray). However the internal state of the referenced object change, each time removeLastObject is called.
Think of it like a house - which has a street address (the reference to the object), and some people in it (the internal state of the object) - as people enter and leave the house (items pushed and popped from the stack) then the internal state keeps changing, but the street address of the house never changes.
HTH.

it goes into multiply if statement sends stack (3,5) returns 5 then on the second call sends(3,5) and returns 5 again?
No. After the first call the stack is reduced. On the second call it sends only (3). Relevant code modifying the stack:
if (topOfStack) [stack removeLastObject];

Related

how to create an autorelease object

Is this method create an autorelease object?
- (instancetype)autoreleasePerson {
return [[Person alloc] init];
}
I created an Command Line Tool Project to test this:
int main(int argc, const char * argv[]) {
#autoreleasepool {
{
[Person autoreleasePerson];
}
NSLog(#"did out scope");
NSLog(#"will out autoreleasepool");
}
NSLog(#"did out autoreleasepool");
return 0;
}
And the output is:
2022-02-04 23:22:23.224298+0800 MyTest[8921:4007144] did out scope
2022-02-04 23:22:23.224771+0800 MyTest[8921:4007144] will out autoreleasepool
2022-02-04 23:22:23.224876+0800 MyTest[8921:4007144] -[Person dealloc]
2022-02-04 23:22:23.224948+0800 MyTest[8921:4007144] did out autoreleasepool
The person instance will dealloc when the autoreleasepool drains!
But when I use the same Person class in my iOS APP project:
- (void)viewDidLoad {
[super viewDidLoad];
{
[Person autoreleasePerson];
}
NSLog(#"out scope");
}
The output is:
2022-02-04 23:28:13.992969+0800 MyAppTest[9023:4011490] -[Person dealloc] <Person: 0x600001fe8ff0>
2022-02-04 23:28:13.993075+0800 MyAppTest[9023:4011490] out scope
The person instance released once out of scope!
Why is this so?
It looks like on macOS the default behaviour is to autorelease return values, except for cases where the method name starts with "new", "init" or "copy":
+ (Person *)createPerson {
return [Person new]; // autorelease & return
}
+ (Person *)newPerson {
return [Person new]; // direct return
}
To control this behaviour apply a compiler attribute:
+ (Person *)createPerson __attribute__((ns_returns_retained)) {
return [Person new]; // direct return
}
+ (Person *)newPerson __attribute__((ns_returns_not_retained)) {
return [Person new]; // autorelease & return
}
To check whether a call to objc_autoreleaseReturnValue was added by the compiler, enable Debug -> Debug Workflow -> Always Show Disassembly,
and put a breakpoint inside these methods on return lines. A call to objc_autoreleaseReturnValue should be visible then:
See ARC reference - Retained return values
Both of the results are valid. You should never assume that there is an autorelease in ARC. See the section "Unretained return values" in the ARC specification:
A method or function which returns a retainable object type but does
not return a retained value must ensure that the object is still valid
across the return boundary.
When returning from such a function or method, ARC retains the value
at the point of evaluation of the return statement, then leaves all
local scopes, and then balances out the retain while ensuring that the
value lives across the call boundary. In the worst case, this may
involve an autorelease, but callers must not assume that the value is
actually in the autorelease pool.
So maybe it's autoreleased, and maybe not (i.e. maybe ARC optimizes it out).
Here, ARC will call objc_autoreleaseReturnValue() when returning from autoreleasePerson, because +alloc returns a retained reference, but autoreleasePerson returns a non-retained reference. What objc_autoreleaseReturnValue() does is check to see if the result of the return will be passed to objc_retainAutoreleasedReturnValue() in the calling function frame. If so, it can skip both the autorelease in the called function, and the retain in the calling function (since they "cancel out"), and hand off ownership directly into a retained reference in the calling function.
objc_retainAutoreleasedReturnValue() is called when ARC will retain the result of a function call. Now, I don't know why in this case calling [Person autoreleasePerson]; will involve a retain of the result, since the result is unused. Perhaps the compiler is treating it as Person temp = [Person autoreleasePerson];, and thus retains and then releases it. This may seem unnecessary, but it is valid for ARC to do it this way. And if ARC does happen to treat it this way internally, then the optimization described above can skip both the autorelease and retain, and it will be simply released in the calling function. Maybe it's doing this in one of your cases and not the other. Who knows why? But my point is that both are valid.
See this article for a more detailed explanation.

Check if MKPolyline overlay already exists

How can I check if a polyline has already been added to the map?
I've tried the following code, but it doesnt seem to work
for (MKPolyline *feature1 in self.mapView.overlays) {
NSLog(#"feature1.title: %#", feature1.title);
NSLog(#"Polu.title: %#", polu.title);
if (![feature1.title isEqualToString:polu.title]) {
NSLog(#"NOT");
[self.mapView addOverlay:polu];
}
else {
NSLog(#"Already added");
}
}
}
I've also tried this:
if (![self.mapView.overlays containsObject:polu]) {
NSLog(#"NOT");
[self.mapView addOverlay:polu];
}
The current for loop assumes the overlay exists or doesn't exist as soon as it finds one other overlay whose title doesn't match.
But at that point, the for loop might not have checked the remaining overlays (one of which might be the overlay you're looking for).
For example:
Assume there are already 4 overlays on the map with the titles A, B, C, and D.
Assume the overlay you want to check for (polu) has the title C.
The first overlay checked is A. Since A does not match C, the existing code immediately adds another overlay named C.
Then the for loop continues and looks at B. Again, since B does not match C, the existing code adds another overlay named C.
Then the loop continues, looks at C, and logs "already added".
Then the loop continues, looks at D, sees it doesn't match C, and adds another overlay.
Instead, you want to stop the loop when a matching title is found and if the loop ends with no match found, then add the overlay.
Example:
BOOL poluExists = NO;
for (MKPolyline *feature1 in self.mapView.overlays) {
NSLog(#"feature1.title: %#", feature1.title);
NSLog(#"Polu.title: %#", polu.title);
//STOP looping if titles MATCH...
if ([feature1.title isEqualToString:polu.title]) {
poluExists = YES;
break;
}
}
//AFTER the loop, we know whether polu.title exists or not.
//If it existed, loop would have been stopped and we come here.
//If it didn't exist, loop would have checked all overlays and we come here.
if (poluExists) {
NSLog(#"Already added");
}
else {
NSLog(#"NOT");
[self.mapView addOverlay:polu];
}
In the second example in the question, containsObject: will only work if polu was the original object that was given the first time addOverlay was called because in this case, containsObject: will compare pointer addresses and not the title property of the overlays.

Objective-C and integers outside methods

I have a question regarding integers outside of methods in Objective-C/Xcode. I'm trying to create a simple guessing game, however my randomizer randomize number every time when the method is called, here is the code snipped:
- (IBAction)guessButton:(id)sender {
int tempUserGuess = [self.textField.text integerValue];
int randomNumber = (arc4random() % 11);
if(tempUserGuess == randomNumber){
self.guessAns.text = #"you won!";
}
if (tempUserGuess < randomNumber){
self.guessAns.text = #"no! too low!";
}
if (tempUserGuess > randomNumber){
self.guessAns.text = #"no! too high!";
}
}
The reason why I'm trying to put an int outside of the method is of that once randomized integer should not be randomized every single time (of course). By the way, everything works fine, the app compiles and works but every single time when I hit return, it randomizes the number.
I know how to do this in Java, but Objective-C seems to be more complex.
Your guessButton method is probably a member of some class. You need to add property to that class holding that randomized number.
You only have to create/store a random number once per game play. There is no need to call the randomize method each time the guess button is pressed. The guessButton method is basically like an ActionListener in java. Each time the button is pressed, whatever's inside the curly braces will be executed. If you add a play again button to the game, then you might want to call the randomize method inside of it's action method.
if(tempUserGuess == num){
self.guessAns.text = #"you won!";
}
This will be a good way because it will be using less memory in the sytem when trying to divert the random number. Good work
Okay, I got it.
Here is the answer:
in ViewController.h I had to include:
NSInteger num;
and then in ViewController.m a method that simply returns a number:
-(int)giveRandom {
int randomNumber = (arc4random() % 10);
return randomNumber;
}
And then refer to 'num' in the method with if statements such as:
...
if(tempUserGuess == num){
self.guessAns.text = #"you won!";
}
...
For some reason it returns 0, but I will try to solve it.
Thanks!

cannot insert into NSMutableArray due to indexvalue being out of bounds

I have a MutableArray on this view called array and the object in question is detailItem, which has a property of rank (int). On this view, there's a text field displaying the rank and I want to be able to move the detailItem up and down the MutableArray by changing the rank.
So, for example let's say the detailItem has a rank of 3, which is index value of 2. If I change this in the text field to 3, I want the array to adjust and move it down one place. However, as I type in the value of rankField (the text field), it crashes the app since it automatically updates the value before I'm done editing. So, if I click on the text field and write 23 (planing on deleting the 2) or just press delete (now the value is nil) the app crashes with an uncaught exception.
Here's the code:
- (IBAction)rankFIeldTextChanged:(id)sender {
QueueMember *member = self.detailItem;
[self.array removeObjectAtIndex:self.detailItem.rank];
if (0<= [self.rankField.text intValue]<= self.array.count) {
[self.array insertObject:member atIndex:[self.rankField.text intValue]-1];
}
}
The if condition of making the text value in-between the array size and 0 seems to have no effect.
btw this is all in the detailsViewController which is connected to the main view controller via push segue. does it make more sense(or more better coding) to just set the new rank value in details and actually make the array changes in the mainviewcontroller.m?
The problem is that you are trying to do two boolean statements at once (which doesn't work). Change your if statement to something like:
if (0< [self.rankField.text intValue] && [self.rankField.text intValue] < self.array.count) {
//Insert your object here
}
else
{
//Add object here
}
Your current setup check to see if 0<= [self.rankField.text intValue], which will return true for all values greater than or equal to 0. Then it checks the result of that (YES:1, NO:0) if it's less than or equal to your array count. That will always return true if your array has anything in it. So basically your check will always return true.
Since it always returns true I could check for array object number 1000, your if statement says go for it, then I check and the array says "No way in heck!" and crashes your app.
EDIT: Updated my code snippet to take into account your array insertion line.
I'd just do this
- (IBAction)rankFIeldTextChanged:(id)sender {
QueueMember *member = self.detailItem;
[self.array removeObjectAtIndex:self.detailItem.rank];
if ((0<= [self.rankField.text intValue])&&([self.rankField.text intValue]<= self.array.count)) {
if (self.array.count >([self.rankField.text intValue]-1)){
[self.array insertObject:member atIndex:[self.rankField.text intValue]-1];
}
}
}
you are probably trying to insert at an index greater than the count of the array.

How to continuously update MKAnnotation coordinates on the MKMap

I have a GPSTestViewController class that has a MKMapView with added MKAnnotations (stored in a class called Bases). I'm trying to continuously update the coordinates for the MKAnnotations (with the updateBaseInformation method in Bases) so the bases is moving on the map. The update is invoked from the GPSTestViewController method locationUpdate (since it's called every second):
- (void)locationUpdate:(CLLocation *)location {
NSLog(#"locationUpdate");
self.cachedLocation = location;
[self centerTo:cachedLocation.coordinate];
//Trying to update the coordinates every second
[bases updateBaseInformation]; <--Program received signal: “EXC_BAD_ACCESS”
return;
}
But then I get the following message:
[bases updateBaseInformation]; Program received signal: “EXC_BAD_ACCESS”
Bases.m contains the following code (and even when some code is commented it crashes):
- (void)updateBaseInformation {
NSLog(#"Updating base information");
for(MyAnnotation *a in bases)
{
//CLLocationCoordinate2D c;
if([a.type compare:#"friend"] == 0)
{
//c.latitude = a.coordinate.latitude+0.001;
//c.longitude = a.coordinate.longitude+0.001;
//a.coordinate = c;
}
else if([a.type compare:#"enemy"] == 0)
{
//[a setCoordinate:CLLocationCoordinate2DMake(a.coordinate.latitude+0.002, a.coordinate.longitude+0.0012)];
}
}
}
My guess is that I'm accessing the objects that are already accessed somewhere else and that causes the EXC_BAD_ACCESS. I have spent many hours on searching, but without results. Why do I get this error and how should I do in order to make the Annotations move around on the map?
I have uploaded the complete project to (link removed).
Solution
The problem is now solved. The problem was that the array that holds the Annotations was autoreleased. So I changed the allocation from
bases = [NSMutableArray array];
to
bases = [[NSMutableArray array] retain];
Thanks in advance.
My guess is that I'm accessing the objects that are already accessed
somewhere else
You are probably accessing an object that no longer exists in memory - You need to check you are not deallocating an object which you are subsequently accessing. You should turn on Zombies for your scheme which will allow you to see when you are accessing a deallocated object by effectively keeping an object's memory under watch after the object has been deallocated. You can switch them on here: Scheme/edit scheme/enable zombie objects.

Resources