I'm trying to create recursion using blocks. It works for a while, but eventually it crashes and gives me a bad access exception. This is my code:
BOOL (^Block)(Square *square, NSMutableArray *processedSquares) = ^(Square *square, NSMutableArray *processedSquares) {
[processedSquares addObject:square];
if (square.nuked) {
return YES; // Found a nuked square, immediately return
}
for (Square *adjacentSquare in square.adjacentSquares) {
if ([processedSquares containsObject:adjacentSquare]) {
continue; // Prevent infinite recursion
}
if (Block(adjacentSquare, processedSquares)) {
return YES;
}
}
return NO;
};
__block NSMutableArray *processedSquares = [NSMutableArray array];
BOOL foundNukedSquare = Block(square, processedSquares);
Explanation: I have a Square class that has a BOOL nuked. It also has an NSArray adjacentSquares containing other Squares.
I want to check whether a square, or one of its 'connected' squares, is nuked or not.
The array processedSquares is to keep track of the squares I have checked to prevent infinite recursion.
When I run this, it's doing a lot of calls of this block (as expected). But at some point, it crashes at the last line with a bad access exception.
I also get this in the console:
Cannot access memory at address 0x1
Cannot access memory at address 0x1
Cannot access memory at address 0x1
Cannot access memory at address 0x1
warning: Cancelling call - objc code on the current thread's stack makes this unsafe.
I'm not that familiar with blocks and recursion. Any ideas?
Edit 1
As requested, the backtrace:
#0 0x00000001 in ??
#1 0x000115fb in -[Square connectedToNukedSquare] at Square.m:105
#2 0x00010059 in __-[Bot makeMove]_block_invoke_1 at Bot.m:94
#3 0x91f3f024 in _dispatch_call_block_and_release
#4 0x91f31a8c in _dispatch_queue_drain
#5 0x91f314e8 in _dispatch_queue_invoke
#6 0x91f312fe in _dispatch_worker_thread2
#7 0x91f30d81 in _pthread_wqthread
#8 0x91f30bc6 in start_wqthread
You need a __block on Block, change the declaration to:
__block BOOL (^Block)(Square *square, NSMutableArray *processedSquares);
Block = ^(Square *square, NSMutableArray *processedSquares) {
When a variable (Block) is referenced within a block then its current value is copied into the block. In your code Block hasn't yet been given a value, as you are constructing the block in the assignment...
The __block prefix passes the variable by reference - by the time your block makes its recursive call Block has a value, the reference to it is used to obtain that value, and the recursive call is OK.
I don't know why its worked at all for you without the __block - failed straightaway for me. With the modifier however I can recurse to at least a depth of 10,000 - so stack space isn't a problem!
You're liking doing something wrong with the setup -- your Square objects are probably messed up somehow. Here's a complete example that works fine for me, maybe it can help you find your mistake:
#include <stdio.h>
#include <Foundation/Foundation.h>
#interface Square : NSObject
{
BOOL nuked;
NSArray *adjacentSquares;
}
#property(nonatomic) BOOL nuked;
#property(nonatomic, retain) NSArray *adjacentSquares;
#end
#implementation Square
#synthesize nuked;
#synthesize adjacentSquares;
#end;
BOOL (^Block)(Square *square, NSMutableArray *processedSquares) = ^(Square *square, NSMutableArray *processedSquares) {
[processedSquares addObject:square];
if (square.nuked) {
return YES; // Found a nuked square, immediately return
}
for (Square *adjacentSquare in square.adjacentSquares) {
if ([processedSquares containsObject:adjacentSquare]) {
continue; // Prevent infinite recursion
}
if (Block(adjacentSquare, processedSquares)) {
return YES;
}
}
return NO;
};
int main(int argc, char **argv)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Square *s1, *s2;
s1 = [[Square alloc] init];
s2 = [[Square alloc] init];
s1.adjacentSquares = [NSArray arrayWithObjects:s2, nil];
s2.adjacentSquares = [NSArray arrayWithObjects:s1, nil];
__block NSMutableArray *processedSquares = [NSMutableArray array];
BOOL foundNukedSquare = Block(s1, processedSquares);
printf("%d\n", foundNukedSquare);
[s1 release];
[s2 release];
[pool release];
return 0;
}
You seem to be adding squares to the array while traversing the array. I'm speaking of this line:
[processedSquares addObject:square];
Might that have to do with it? You're adding an object while traversing. I'm surprised that this works at all.
Related
(This is a work in progress. I wonder if someone could to improve it)
in Objective C, it's easy to resolve a hostname with NSHost.
[[NSHost hostWithName:#"www.google.com"] address]
Sadly iOS (iPhone) contains only a private version of NSHost.
I found many ways of doing this with other Objects or methods, but all of them got only IPv4 addresses in the results. So here is for the moment the only efficient method I have found.
I first tried to use the asynchronous CFHostStartInfoResolution as did bdunagan, but failed to adapt it to IPv6.
Some of you will appreciate to get a method working, so here is one, but if you know a way which would be Asynchronous I would appreciate to learn about it... cause for the moment I use a Popup to alert about the next freeze that could occur with slow cellular connection
/**
Give the IPs corresponding to a Hostname
Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached
#author Christian Gonzalvez, http://wiki.gonzofamily.com
#param hostName A hostname
#return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
const char* hostnameC = [hostname UTF8String];
struct addrinfo hints, *res;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
int retval;
char buf[64];
NSMutableArray *result; //the array which will be return
NSMutableArray *result4; //the array of IPv4, to order them at the end
NSString *previousIP = nil;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;//AF_INET6;
hints.ai_flags = AI_CANONNAME;
//AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST
//AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED
retval = getaddrinfo(hostnameC, NULL, &hints, &res);
if (retval == 0)
{
if (res->ai_canonname)
{
result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]];
}
else
{
//it means the DNS didn't know this host
return nil;
}
result4= [NSMutableArray array];
while (res) {
switch (res->ai_family){
case AF_INET6:
s6 = (struct sockaddr_in6 *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))
== NULL)
{
NSLog(#"inet_ntop failed for v6!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
case AF_INET:
s4 = (struct sockaddr_in *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf))
== NULL)
{
NSLog(#"inet_ntop failed for v4!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result4 addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
default:
NSLog(#"Neither IPv4 nor IPv6!");
}
//surprisingly every address is in double, let's add this test
previousIP = [NSString stringWithUTF8String:buf];
res = res->ai_next;
}
}else{
NSLog(#"no IP found");
return nil;
}
return [result arrayByAddingObjectsFromArray:result4];
}
NB: I noticed that most of the time only 1 IPv6 is returned, I suspect it's due to iOS Memory optimisation when locally cached. if you run this method again and again, sometime you have 3 IPv6, but then you have only 1 IPv4.
If you want a method to run on a background thread, the simplest way is to use performSelectorInBackground:withObject:; this is an instance method of NSObject, so any object can use it without any extra work (including, interestingly enough, class objects, which is good in this case because this is a class method):
[[self class] performSelectorInBackground:#selector(addressesForHostName:)
withObject:theHostName];
Inside the method, you will need to set up an autorelease pool for the thread. You will also need some kind of callback method set up to get the return value back to your main thread. Make sure that you don't try to do any GUI activity on the background thread. It's only safe to do that on the main thread.
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Do your stuff...
// Wait until done to allow memory to be managed properly
// If we did not do this, the array might be deallocated
// before the main thread had a chance to retain it
[self performSelectorOnMainThread:#selector(addressesCallback:)
withObject:[result arrayByAddingObjectsFromArray:result4]
waitUntilDone:YES];
// Inside a class method, self refers to the class object.
[pool drain];
}
If you were not on the main thread to begin with, or if you needed more control, you could also look into NSOperation, which is more powerful and therefore requires more work. It's still easier than explicit thread management, though!
Hope that solves your problem. It sounded like you have this method doing what you need, you just need it to not block the main thread.
thanks to Josh I could do it, but here is what I had to do :
Instead of calling directly
self.ipAddressesString = [CJGIpAddress addressesForHostname:#"www.google.com"];
I call
[self resolveNow:#"www.google.com"];
And create 3 new methods:
- (void)resolveNow:(NSString *)hostname
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self performSelectorInBackground:#selector(hostname2ipAddresses:)
withObject:hostname];
}
- (void)hostname2ipAddresses:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//Here is my previous lonely line !! safely started in an other thread
self.ipAddressesString = [CJGIpAddress addressesForHostname:hostname];
[self performSelectorOnMainThread:#selector(resolutionDidFinish)
withObject:nil
waitUntilDone:YES];
[pool drain];
}
- (void)resolutionDidFinish
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
//My STUFF with self.ipAddressesString (now filled)
}
Edit:
In practice I use all of that in a Model, so I had a crash when I close the View before the end of the resolution
So in the view I added in dealloc what is necessary to avoid a crash
- (void)dealloc
{
self.model.delegate = nil;
[super dealloc];
}
Then - in the model - I test delegate before doing anything with it.
Header :
#interface CodeTest : NSObject {
BOOL cancelThread;
}
-(void) testCode;
-(void) stopRunning;
-(void) startRunning;
#property (assign) BOOL cancelThread;
#end
Implementation :
-(void)stopRunning{
self.cancelThread = YES;
}
-(void)startRunning{
self.cancelThread = NO;
[NSThread detachNewThreadSelector:#selector(testCode) toTarget:self withObject:nil];
}
-(void)testCode{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"STARTED");
/* Std C99 code */
while( conditions ){
if(self.cancelThread){
conditions = NO;
}
...
...
}
/* end of C code */
[pool release];
}
As you can see, it tires to execute testCode on a separate thread and using the cancelThread BOOL as a stop execution flag in the separate thread.
I have the following issue :
The compiler signals that self.cancelThread in the middle of the std c code is not valid (self is not defined). I think, it tries to interpret that statement as c code but this is obj-c.
There is something missing ?
UPDATE : It's not related to missing {}'s as one of you suggested... The code works perfectly without the if(self.cancelThread){ conditions = NO; }.
-[CodeTest setCancelThread:]: unrecognized selector.
Means that you don't have a setter defined for the cancelThread property. You're missing
#synthesize cancelThread;
(in your #implementation section)
What do you mean by " /* Std C99 code */"?
If that code is really being compiled as C99 code, then self.cancelThread is problematic because it is an Objective-C expression. First, it is the equivalent of [self cancelThread], a method call and, secondly, it requiresself` which wouldn't be present in the body of a C99 function.
However, given that the code you showed has it in a method, the comment doesn't make sense.
So there's a number of ways to run all the elements of an array through a selector or block of code. makeObjectsPerformSelector: and more
If you have 4 or 16 cores on hand, you may well want to send all the processing off to different processes.
Even on iOS, it could well be sensible to send them all away, or at least let them be finished whichever way the wind blows.
What is the best and neatest way to do this in the cocoa milieu?
Again, what if you want to send them away to be finished at any time, i.e., do not wait for the enumeration to finish before executing the following instruction...?
If you want concurrency and synchronous behaviour (i.e., wait for the enumeration to finish before executing the following instruction), -[NSArray enumerateObjectsWithOptions:usingBlock:] will do that if you pass the NSEnumerationConcurrent option. This method is available on Mac OS 10.6+ and iOS 4.0+.
If you want asynchronous behaviour, you can use one of the standard multithreading solutions such as NSOperation or GCD combined with -enumerateObjectsWithOptions:usingBlock:. For instance,
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[array enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// do something with obj
}];
});
If you can target iOS 4+ or Mac OS X 10.6+, use Grand Central Dispatch (and I'm using a category because I think they're cool):
#import <dispatch/dispatch.h>
#interface NSArray (DDAsynchronousAdditions)
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector;
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector withObject:(id)object;
#end
#implementation NSArray (DDAsynchronousAdditions)
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector {
[self makeObjectsPerformSelectorAsynchronously:selector withObject:nil];
}
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector withObject:(id)object {
for (id element in self) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
[element performSelector:selector withObject:object];
});
}
}
#end
//elsewhere:
[myArray makeObjectsPerformSelectorAsynchronously:#selector(doFoo)];
Or, if you don't want to use a category...
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL * stop) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
[obj performSelector:#selector(doFoo)];
};
}];
I am having a memory leak issue with the following code. As much as I can tell I don't see why the problem persists but it still does not release when called. I am detecting the problem in instruments and the following code is keeping its "cards" classes alive even when it should had released them.
Any help welcome.
...
...
-(id)initDeckWithCardsPicked: (NSMutableArray*)cardsPicked andColors:(NSMutableArray*)cardColors
{
self = [self init];
if (self != nil) {
int count = [cardsPicked count];
for (int i=0; i<count; i++) {
int cardNum = [[cardsPicked objectAtIndex:i] integerValue];
Card * card = [[MemoryCard alloc] initWithSerialNumber:cardNum position: CGPointZero color:[cardColors objectAtIndex:i]];
[_cards addObject: card];
[card release];
}
}
return self;
}
- (id) init
{
self = [super init];
if (self != nil) {
self.bounds = (CGRect){{0,0},[Card cardSize]};
self.cornerRadius = 8;
self.backgroundColor = kAlmostInvisibleWhiteColor;
self.borderColor = kHighlightColor;
self.cards = [NSMutableArray array];
}
return self;
}
...
...
Without looking at the rest of your code, its hard to know where the problem is, but have you tried using the static analyzer in xcode? Its can be invaluable for finding memory leaks.
To use it, select 'Build and Analyze' from the Build menu. Further details are on Apple's dev website.
When you add a Card to the _cards NSMutableArray using addObject, it is sent a retain message. Thus, as long as you keep _cards in memory, a pointer will also be kept for each of it's constituents. As long as your dealloc releases the array, or you do so elsewhere, you shoul dbe fine with what you have posted here (assuming your initWithSerialNumber method returns a retained object).
So I'm trying to do collision detections between sprites in cocos 2d. Although I think I may be asking too much as it crashes and the compiler doesn't give me an error, the iPhone simulator just freezes and then gives up. There's a lot of looping involved so I'm guessing it's just too much... but I can't be certain.
My intention was to work it like this.
Each Sprite can belong to a collision group, so I can be specific about what collides with what.
I have an array called collisionGroups, which contains an array of all Sprites in that group.
I have an array called collisionPairs, which holds Arrays like [1,3][3,7][1,7]...
At regular intervals, I want to loop through all the collisionGroup pairs, and all the sprites in those groups to check for basic CGRectIntersectsRect.
Alas, I never got that far, that's where it crashes, without any helpful advice from the compiler. Before I set about trying to do this out a different way, am I right in thinking that it's just too much to loop through? Or is something else the problem?
Here's all the code for the collision controller.
#implementation CollisionsController
-(id) init
{
if((self == [super init])){
int collisionCapacity = 10;
NSNotificationCenter *NC = [NSNotificationCenter defaultCenter];
[NC addObserver:self selector:#selector(registerSpriteForCollisions:) name:#"REGISTER_SPRITE_FOR_COLLISIONS" object:nil];
collisionGroups = [NSMutableArray arrayWithCapacity:collisionCapacity];
collisionPairs = [NSMutableArray arrayWithCapacity:collisionCapacity];
// fill up the arrays with Arrays to be used
for(int i = 0; i <= collisionCapacity; i++){
NSNumber *dummyValue = [NSMutableArray arrayWithCapacity:100];
[collisionGroups addObject:dummyValue];
}
}
return self;
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-(void) registerSpriteForCollisions:(NSNotification *)sprite
{
GIAtlasSprite *cSprite = [sprite object];
int colIndexInt = [cSprite getCollisionGroup];
[[collisionGroups objectAtIndex:colIndexInt] addObject:cSprite];
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-(void) handleCollisionsBetweenGroup:(int)groupA andGroup:(int)groupB
{
NSNumber *numberA = [NSNumber numberWithInt:groupA];
NSNumber *numberB = [NSNumber numberWithInt:groupB];
BOOL safeToAdd = YES;
for(NSArray *pair in collisionPairs){
if(([pair objectAtIndex:0] == numberA && [pair objectAtIndex:1] == numberB) || ([pair objectAtIndex:0] == numberB && [pair objectAtIndex:1] == numberA)){
safeToAdd = NO;
break;
}
}
if(safeToAdd){
NSArray *pairArray = [NSArray arrayWithObjects:numberA, numberB, nil];
[collisionPairs addObject:pairArray];
}
}
// ------------------------------------------------------------------------------------------------------------------------------------------------------------
-(void) checkCollisions
{
for(NSArray *cp in collisionPairs){
// WHEEEEEEEEE! CRASH!
}
}
#end
The Stack trace gives me this
#0 0x936f768c in objc_msgSend
#1 0x00006b1f in -[CollisionsController checkCollisions] at CollisionsController.m:90
#2 0x00005359 in -[BownceLevel tick:] at BownceLevel.m:103
#3 0x0004fc0d in -[Timer fire:] at Scheduler.m:87
#4 0x000507a6 in -[Scheduler tick:] at Scheduler.m:215
#5 0x0002ca51 in -[Director mainLoop] at Director.m:229
#6 0x96e56483 in __NSFireTimer
#7 0x903a98f5 in CFRunLoopRunSpecific
#8 0x903a9aa8 in CFRunLoopRunInMode
#9 0x31566600 in GSEventRunModal
#10 0x315666c5 in GSEventRun
#11 0x30a4eca0 in -[UIApplication _run]
#12 0x30a5a09c in UIApplicationMain
#13 0x000029a4 in main at main.m:13
I've absolutely no idea where to start with the Stack trace. All I know is that adding the for loop in checkCollisions is what causes the crash.
As always, any help or guidance is appreciated muchly.
collisionGroups and collisionPairs should be retained when you create them or else they will end up being freed by the autorelease pool in the run loop. Change the declarations to:
collisionGroups = [[NSMutableArray arrayWithCapacity:collisionCapacity] retain];
collisionPairs = [[NSMutableArray arrayWithCapacity:collisionCapacity] retain];
and of course, release them in the -dealloc.