Crash while in CALayer -hitTest: - iphone

This issue really has me stumped...
I have an iPad project that I use UIPanGestureRecognizer and I am using the following method call within my handlePanGesture:
- (AIEnemyUnit *) hitTestForEnemyUnit:(CGPoint)where {
CALayer * layer = [self hitTest:where];
while (layer) {
if ([layer isKindOfClass:[AIEnemyUnit class]]) {
return (AIEnemyUnit *)layer;
} else {
layer = layer.superlayer;
}
}
return nil;
}
Once I "find" an AIEnemyUnit layer, I continue with the drag and everything works fine. Except about around the 6th to 10th "drag" I get a crash with the debugger deep within only the CALayer -hitTest:
modifying layer that is being finalized - 0x124530
*** -[NSCFSet hitTest:]: unrecognized selector sent to instance 0x124530
*** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason:
'*** -[NSCFSet hitTest:]: unrecognized selector sent to instance 0x124530'

From the symptoms it looks like you have an over-release of a CALayer.
Two areas you should check:
1) Are you 'saving' this CALayer in a variable without retaining it? If you're hitting any autorelease pools (including the one supplied on the main thread) then these layers could be getting released unintentionally. As noted in the comments, since these aren't autoreleased, this can happen without hitting a pool. It can happen anytime the CALayer is released while you're holding a reference however.
2) You're explicitly calling release on this layer later on. Since you are given this layer as is (both hitTest: and superlayer return objects without an extra retain count) you do not have ownership, and thus should not release it.
Another useful tool for debugging is using NSZombies, and some other techniquies linked there. NSZombies basically allows your application to crash the moment you access a released object, which hopefully will give you a more meaningful stack trace.

I think there's a bit of 'misinformation' in the hitTest documentation actually. I ran into a similar problem myself putting 4 instances of a subclassed view onto the window, each with four sublayers in it. Each of the 4 view subclasses had a touchesBegan:withEvent method and a touchesEnded:withEvent method defined. I found that if my touch landed or ended in the top-left most view, my hitTest returned a valid sublayer. However, hitTests in any of the other three views returned nil for the sublayer. Like you, I was totally stumped until I decided to replace the touch point in the view's co-ordinate system with one for the window's co-ordinate system and then it all worked. I reproduce the documentation for the hitTest method:
hitTest:
Returns the farthest descendant of the receiver in the layer hierarchy (including itself) that contains a specified point.
- (CALayer *)hitTest:(CGPoint)thePoint
Parameters
thePoint
A point in the coordinate system of the receiver's superlayer.
Return Value
The layer that contains thePoint, or nil if the point lies outside the receiver’s bounds rectangle.
Availability
Available in Mac OS X v10.5 and later.
Declared In
CALayer.h
I would argue, based on my observations, that the explanation of 'thePoint' is wrong. I think it should read 'A point in the coordinate system of the window containing the receiver.' I think the only reason the top-left view gave valid hitTests was because the co-ordinates of the touch - in that location - are the same as the co-ordinates of the touch in the window. Don't know if this helps you but it's helped me get my logic working.
V.V.

Related

Trouble with getChildByTag (cocos2d iPhone)

I have a scene called Level1, which takes the hero and the enemy from a layer called GameLayer. I heard that an efficient way of doing this is using tags and retrieving it using getChildByTag. I tried this out, but I'm having many issues. I'm using SneakyInput. On Level1, there is a leftJoystick (for enemy movement), and a rightJoystick (for firing projectiles). I have an addEnemy and addHero method in my GameLayer, which I call. Everything I've mentioned works.
In my Level1 scene I have a method called moveHero (which obviously is supposed to move the hero with the joystick.). Through basic debugging I know the problem is the geteChildByTag line. I test out the hero's position through NSLog, and it's saying 0,0. Which is weird because on screen you can see the hero. But I also realized I'm calling the addHero method without using getChildByTag.
I hope I'm being clear here. I've uploaded GameLayer.h and GameLayer.m onto 4shared. http://www.4shared.com/file/PqhjoMFy/GameLayer.html
Hopefully you can take a look at it and point me in the right direction.
BTW: There are no errors or crashes. It's just not working.
Thanks in advance.
getChildByTag will never crash, it's a pretty nicely coded method that just loops through the children array of the object and checks to see if any objects match, that way you don't get assertion issues.
You have some serious issues here with your code.
Firstly..
GameLayer *heroInstance = [[GameLayer alloc] init];
CCSprite *hero = (CCSprite *)[heroInstance getChildByTag:1];
NSLog(#"Hero position X: %f", hero.position.x);
NSLog(#"Hero position Y: %f", hero.position.y);
This will never work, heroInstance is a brand new object, it has no children, also you've just created a memory leak here.
Your hero is a child of the spritesheet, which is a child of the scene.
To reference your child you must call getChildByTag on your spritesheet (which you probably need to reference by calling getChildByTag on your scene..
something like this.
[[self getChildByTag:spritesheet] getChildByTag:hero];
Also, use an enum, so that you don't have to remember what numbers certain tags are (look at the cocos2d example projects).

iPhone SDK: Block Animation Ignore Error with Curved Path

I'm creating an application where the user can draw a line on a screen from an object to the location they want to send it and the object will then follow the line to the final location. In order to do this, I've already created working methods to allow the user to draw the lines and then to store the coordinates of the line in a MutableArray. However, I'm having some trouble when I try to animate. As I'm pretty new to the iPhone OS, this could be a simple problem, but I haven't been able to find a solution yet.
I am NOT using Bezier Paths as the user is drawing the line manually, I'm not drawing it programmatically.
Here's the code that I've tried
-(void)animateButtonWasPressed
{
for (int f = 0; f < [cordArrayY count]; f++) {
NSString *newY = [cordArrayY objectAtIndex:f];
NSString *newX = [cordArray objectAtIndex:f];
[self myAnimate:newX :newY];
}
}
-(void)myAnimate:(NSString *)PntX :(NSString *)PntY
{
[UIView animateWithDuration:.5 animations:
^{
object.center = CGPointMake([PntX floatValue], [PntY floatValue]);
}];
}
SYNTAX:
object - the object I am trying to move
cordArray - the mutable array containing the x-coordinates
cordArrayY - the mutable array containing the y-coordinates
Everything else is either defined within the code or Apple methods
The problem: the object moves instantly from its original location directly to the final location. I get a NSLog which tells me this:
-[UIApplication endIgnoringInteractionEvents] called without matching -beginIgnoringInteractionEvents. Ignoring.
Any help would be appreciated!
The method you're using to animate "object" seems to be ok. I believe the problem is the loop in which you are invoking that method. You are trying to animate the same property of the object over and over in every step of that loop. I think this causes that "jump".
Take a look at this quote from Apple's docs:
Important: Changing the value of a property while an animation
involving that property is already in progress does not stop the
current animation. Instead, the current animation continues and
animates to the new value you just assigned to the property.
http://developer.apple.com/library/ios/#documentation/WindowsViews/Conceptual/ViewPG_iPhoneOS/AnimatingViews/AnimatingViews.html#//apple_ref/doc/uid/TP40009503-CH6
As a consequence of invoking the animation in each step, you will end up animating object in approximately 0.5 secs to the last position of your coordinates array.
I think you should link those animations together, but you should wait for each animation to finish to start the following one. Take a look at this
Another thing that both the animateWithDuration:animations:completion:
and animateWithDuration:delay:options:animations:completion: methods
support is the ability to specify a completion handler block. You
might use a completion handler to signal your application that a
specific animation has finished. Completion handlers are also the way
to link separate animations together.
Hope this helps,
Cheers.
I was finally able to solve this problem:
At first I tried to continue using the method that I had above. Lio's advice to use the "completion" block was perfect, but as I needed it to loop for an undefined number of times, I would have to use a counter variable. iPhone block programming doesn't allow the modification of external variables or the use of the _block declaration, so this didn't work out for me.
However, I eventually created my entire animation using NSTimer in the method described here just using my array coordinates instead:
http://www.icodeblog.com/2008/10/28/iphone-programming-tutorial-animating-a-ball-using-an-nstimer/

Scenes not completly released in Cocos2d iPhone application - debugging

I have an iPhone application in Cocos2d that sometimes crashes on actual device, due to memory problems.
What I have so far found out is that the scenes, when switched, aren't fully released - the [retainCount] for them is somewhat about 4-10 :)
The dealloc method never gets called, and then I assume, when I switch the scenes a few times, the memory issue shows up.
I'm wondering - where should I relese the scene? Since it has number of children, I suppose I should be doing a cleanup-removal of them. But it turns out that removing all children from the layer doesn't decrease the retain count of it. I added such a piece of code to my cleanup method:
- (void) cleanup {
while ([self.children count] > 0) {
CCLOG(#"child: %d - %# rc: %d", 0, [self.children objectAtIndex:0], [[self.children objectAtIndex:0] retainCount]);
[self removeChild:[self.children objectAtIndex:0] cleanup:YES];
}
[super cleanup];
}
But then the [self retainCount] method still returns a number greater then 1 or 0, and my dealloc doesn't get called.
Is there something I should be doing in order to release those children properly? If I add my own subclass of CCSprite as child, should I do something specific in that class' release or dealloc method, other then just calling it's [super] method?
Do not call retainCount
retainCount is useless, as you've discovered, when dealing with complex frameworks. There are any number of internal implementation details that could cause the retain count to be an unexpected value at any given time without indicating a bug.
You should release the scene to balance however many times you retained the scene, no more and no less.
If you release it more times than you retained it, you're app will likely crash whenever you [potentially accidentally] fix the real problem.
In general, when dealing with a hierarchy of items like views, layers, or sprites, you remove the root view/layer/sprite and that removal takes care of tearing down the hierarchy (including releasing as needed).
That assumes that you haven't retained anything in the hierarchy. If you have, then you need to also release those references when the root is removed and released.
Usually you don't have to release your children by yourself. How do you add your child?

Tricky memory management in objective-c

After reading:
Memory management of a view controller in Objective-c
and
Does UIView's addSubview really retain the view?
I wrote the following code to toggle a subview:
#synthesize switchableView, viewSelector, currentSubview;
//...
if(switchableView.subviews.count != 0)
[[switchableView.subviews objectAtIndex:0] removeFromSuperview]]
self.currentSubview = (veiwSelector.selectedSegmentIndex == 0) ?
[ViewA new] : [ViewB new];
[switchableView addSubview:currentSubview.view];
//[currentSubview release]; //<---crashes if I uncomment this line
It seems to run fine if I comment out that release line, but I can't wrap my head around why. Here's the way I understand what happens and maybe someone can tell me where I go wrong:
So lets consider currentView:
A gets alloc-ed by the 'new' message--retain count=A:1
A gets retained by the setter--retain count=A:2
A's view gets (supposedly) retained--retain count=A:2.1
next time through...
A's subview gets released count=A:2
B gets alloc-ed by the 'new' message--retain count=B:1, A:2
A gets autoreleased by the setter-- B:1, A:1
B gets retained by the setter--B:1, A:1
nothing ever gets rid of A?
So should I change my code, or am I wrong about the way memory management works in this language...or both?-
Ok, step one, ignore the retainCount. It's one of those things Apple should rename to something like lsdjiofsudfoiwjeriowhfiuwhrteiuhweifhsdjkfhsiurwoieuriosfho so people won't guess it's name, and not list it in the documentation. For your purposes, it's entirely useless, so ignore it.
Now that I've said that, let's consider something: addSubview: DOES retain its argument, and removeFromSuperview releases the receiver.
Finally, it's hard to tell what currentSubview is. It has a view property which would lean towards a VC, however, the way you're using it by itself, would indicate its a normal view. Perhaps you can clarify so I can continue my answer.
Your understanding of retain and release is correct, as is your code. That suggests that the problem lies outside of the code you've posted. For example, you would have this problem if your currentSubView property was defined as assign instead of retain.
You code is not structured well, however. This would be much clearer:
self.currentSubView = [[ViewA new] autorelease];
Furthermore, view controllers are meant to be cached, not created and released each time the user toggles a display. Typically, you create your view controllers beforehand, and access their .view property when necessary to display the view. UIViewController will automatically deallocate non-visible views in low memory conditions, and re-allocate their view when the .view property is accessed.
Change the release line to
self.currentSubview = nil;
and I think you'll be fine. You're releasing, but not setting the property to nil. So, when it gets re-assigned next time through, release will be called again on it. But you already released it so... boom.

_NSAutoreleaseNoPool Breaking but No Helpful Stack Trace

I am getting the message:
*** _NSAutoreleaseNoPool(): Object 0x3f43660 of class UICFFont
autoreleased with no pool in place -
just leaking
I have placed a break point using the symbol _NSAutoreleaseNoPool and the program does break, however, the stack trace does not show me any of my code only some UIView and Core Animation layer code.
alt text http://img.skitch.com/20100614-fw7u4qtb5bprpwrkh9rdkwn3rq.png
Is there a better way to get to the bottom of the issue? There is apparently a thread that does not have an auto release pool, but I can't figure out where.
Thanks.
Are you using CATiledLayer instances ? This is the only type of layer I know that can have their drawLayer:inContext: method called from an arbitrary thread:
As more data is required by the
renderer, the layer's
drawLayer:inContext: method is called
on one or more background threads to
supply the drawing operations to fill
in one tile of data. The clip bounds
and CTM of the drawing context can be
used to determine the bounds and
resolution of the tile being
requested.