Im getting location updates from a locationManager with this method:
- (void) locationManager:(CLLocationManager *) manager
didUpdateLocations:(NSArray *)locations
{
for (CLLocation *p in taggedObjectsArray) {
CLLocationDistance distance = [p distanceFromLocation: newLocation];
if(distance < 10.00){
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
// if warning setting is set to sound, play sound
if (beep) {
AudioServicesPlaySystemSound(beep);
}
[taggedObjectsArray removeObject:p];;
}
}
The taggedObjectsArray contains pointers to Cllocation objects
Now it works perfectly as long as there at not two objects in the array at any given run through the for loop. When there is two(or more) the app crashes after playing a single vibration.
Im guessing its because its trying to play a sound/beep on top of another sound/beep, but im not sure.
Anyone have a simple solution to this?
Thanks!
Agree with LuisCien that posting the contents of the debugger from the crash would be helpful.
One (blind) suggestion though would be to hold off on removing each object from taggedObjectsArray during each iteration of the loop and just do it after the fast enumeration completes with a single call to [taggedObjectsArray removeAllObjects];
from a pure efficiency and readability standpoint, it's cleaner to do this once with a stock method designed to remove everything than to invoke a single removal p times, since both end up with the same thing (an empty NSMutableArray)
There's a chance (speculation) that it might solve your crash too, but posting the crash log will help decide that better.
Related
I'm having a really strange issue with GeoFire. I'm going to bet that I caused an issue somewhere but right now, with the very limited amount of code change, 2 major issues have come up:
1) GeoFire GFQuery is taking a LONG time to respond. It is taking upwards of 3 minutes for GFQuery to finally return the ONLY key sitting in a small, 300 meter circle. The only code I have added to start this query is this:
if (!self.circleQuery) {
GeoFire *geoFire = [FirebaseUtils GEOFIRE_REF];
self.circleQuery = [geoFire queryAtLocation:center withRadius:0.3];
[self.circleQuery observeEventType:GFEventTypeKeyEntered withBlock:^(NSString *key, CLLocation *location) {
}];
[self.circleQuery observeEventType:GFEventTypeKeyExited withBlock:^(NSString *key, CLLocation *location) {
}];
[self.circleQuery observeEventType:GFEventTypeKeyMoved withBlock:^(NSString *key, CLLocation *location) {
}];
} else if (![self.circleQuery.center isEqual: center]){
self.circleQuery.center = center;
}
2) With only the code above having been added, I am seeing a major memory leak that unfortunately instruments is not catching. Normally when the app starts up it idles around 8-9 mgbts in memory. After adding the Geofire code, I see a steady increase that will continue growing up and up. I stashed all changes to remove any geofire code and it removes the leak so I konw it has something to do with this. I have a feeling that both of my problems are VERY connected.
Any help or ideas on this would be greatly appreciated!
UPDATE: It seems whatever I'm doing is also blocking Firebase from making other updates. When this code is implemented, if I delete a child on firebase directly, it fails to pop the childRemoved notification it normally does instantly.
After some further experimentation, I found out that GeoFire does not work well at all if you build it off a Firebase reference to just the base url. By appending "/geofire", or whatever else you might want to call it, to the url, all of my issues went away :). The reason I was using the base url in the first place is that all of the iOS documentation on GeoFire (cocoapods & github) reference the base url and don't say anything about creating a separate node just for geofire. To me this should be made clear...memory leaks and 3 minute response time is a BIG problem.
I have a view with a bunch of UISwitch.
My problem is that when I tap on a switch I need to wait about 10 seconds before being able to tap any other switch of the view.
Here is my code :
-(void) didTapSwitch:(UISwitch *)sender
{
NSLog(#"BEGIN didTapSwitch, %#",sender);
DADudesManager *dudesManager = [DADudesManager getInstance];
DADude *updatedDude = [dudesManager.dudesList objectAtIndex:[[self.spendingDudesTableView indexPathForCell:sender.superview.superview] row]];
DAAccountManager *accountManager = [DAAccountManager getInstance];
[accountManager.accountsOperationQueue addOperationWithBlock:^{
NSLog(#"BACKGROUND OPERATION BEGINS switchDudeBeneficiates, %#",sender);
DASpendingsManager *spendingsManager = [DASpendingsManager getInstance];
[[spendingsManager.spendingObserver childByAppendingPath:self.spending.spendingID] updateChildValues:#{updatedDude.dudeName: [sender isOn] ? #"1" : #"0"}];
NSLog(#"BACKGROUND OPERATION ENDS switchDudeBeneficiates, %#",sender);
}];
NSLog(#"END switchDudeBeneficiates, %#",sender);
}
My spendingObserver is a Firebase object initiated before.
When the code above is executed, the NSLogs show almost instantaneously in the console, the data is updated online at the same time, but the switches don't react to any tap for another 9 to 11 secs.
Of course commenting the line [[spendingsManager.spendingObserver childByAppendingPath:self.spending.spendingID] updateChildValues:#{weakDude.dudeName: [weakSwitch isOn] ? #"1" : #"0"}]; removes the latency, so the problem must come from Firebase, but I have no clue what's going on.
I am probably missing something obvious as I'm pretty new to IOS development !
I can think couple of reasons.
You are sending the PayLoad in the main thread, which is causing the User INterface events to be suspended.
The code you ran, might be linked to other functions in the library you are using, that might be causing the lag.
TRY - >
try putting your code in an NSOperation and execute that. Or use GCD to do work on different thread just not the UI thread which is the main thead.
Step back and simplify. Make your switch code simply log the change in value. NSLog includes a timestamp, so you can tell when the switch events occur.
If do-nothing code responds quickly, as I suspect it will, then add log statements at the beginning and end of your switch action method. That way you can see if there is a delay between the beginning and end of the processing.
You could also run the app in instruments (time profiler) and see where your app is spending time.
I have an iPhone/iPad app where one of the main functions is to arrange objects on a plot. I use Core Data to manage the objects' relationships; for this example, I'll talk about Units, which have a to-one relationship to a Plot (and a to-many inverse). Units' properties include positionX, positionY, and angle.
Each Unit (inherits from NSManagedObject) is paired with a UnitViewController (inherits from UIViewController). The Unit has a property .viewController and the UnitViewController has a property .object, so in different uses they can refer to each other. These are set when the Plot is opened or new Units are added (or re-added from Undo, etc).
Each UnitViewController has a UIPanGestureRecognizer for its view, and when that gesture occurs, the UnitViewController changes its .object's positionX and positionY values. When that happens, the UnitViewController then observes those changes through KVO and re-positions the view.
This may seem convoluted, but the reason I did it this way is that I can also change the position numerically in a UITableView. Here's an abbreviated version of the code, showing the crucial bits of the path. Some of these methods exist in my UIViewController+LD category, hence the general names.
- (IBAction)dragObject:(UIPanGestureRecognizer *)gesture
{
// GESTURE BEGAN
if ([gesture state] == UIGestureRecognizerStateBegan) {
[self beginGesture:gesture];
if (!_selected) [self setSelected:YES];
}
// turn on registration before last pass
if ([gesture state] == UIGestureRecognizerStateEnded || [gesture state] == UIGestureRecognizerStateCancelled) {
[[NSNotificationCenter defaultCenter] postNotificationName:ENABLE_UNDO_REGISTRATION object:nil];
}
// MOVE
[self dragUnitWithGesture:gesture];
// turn off registration after first pass
if ([gesture state] == UIGestureRecognizerStateBegan) {
[[NSNotificationCenter defaultCenter] postNotificationName:DISABLE_UNDO_REGISTRATION object:nil];
}
// GESTURE ENDED
if ([gesture state] == UIGestureRecognizerStateEnded ||
[gesture state] == UIGestureRecognizerStateCancelled) {
[self endGesture];
}
}
- (void)dragUnitWithGesture:(UIPanGestureRecognizer *)gesture
{
CGPoint translation = [gesture translationInView:self.view.superview];
[self saveNewObjectCenterWithTranslation:translation];
}
- (void)saveNewObjectCenterWithTranslation:(CGPoint)translation
{
[self saveNewObjectCenter:CGPointMake(initialCenter.x + translation.x, initialCenter.y + translation.y)];
}
- (void)saveNewObjectCenter:(CGPoint)center
{
CGPoint dataPoint = [Converter dataPointFromViewPoint:center];
self.object.positionX = [NSNumber numberWithFloat:dataPoint.x];
self.object.positionY = [NSNumber numberWithFloat:dataPoint.y];
}
- (void)beginGesture:(UIGestureRecognizer *)gesture
{
[[NSNotificationCenter defaultCenter] postNotificationName:BEGIN_UNDO_GROUPING object:nil];
self.initialCenter = self.view.center;
}
- (void)endGesture
{
NSError *error = nil;
[self.object.managedObjectContext save:&error];
[[NSNotificationCenter defaultCenter] postNotificationName:END_UNDO_GROUPING object:nil];
}
My issue comes from crash reports obtained through Crashlytics, because I cannot replicate the crash on my devices. There have been multiple reports that all occur with the stack trace:
_UIGestureRecognizerSendActions
-[UnitViewController dragObject:]
-[UnitViewController dragUnitWithGesture:]
-[UnitViewController saveNewObjectCenterWithTranslation:]
-[UIViewController(LD) saveNewObjectCenter:]
_sharedIMPL_setvfk_core + 110
-[NSObject(NSKeyValueObserverNotification) willChangeValueForKey:] + 180
NSKeyValueWillChange + 474
NSKeyValuePushPendingNotificationPerThread + 214
This particular one ended with:
Crashed: com.apple.main-thread
EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x00000000
But I've also seen:
Fatal Exception: NSInternalInconsistencyException
An -observeValueForKeyPath:ofObject:change:context: message was received but not handled (keyPath: positionX)
Fatal Exception: NSObjectInaccessibleException
Core Data could not fulfill a fault for ‘0x00000000 <x-coredata://xxxxxx/Unit/p116>'
So my question: is there a known issue with this type of data modification? Is it simply too fast for the Core Data framework to handle? Or is there something else I could be doing wrong? This problem is only one of the ways Core Data issues have manifested in my app, and I'd love to get to the heart of the matter to make my app more stable.
Update:
I don't have enough reputation to post images, but here's a link to the full stack trace:
stackTrace
As much as I agree with Marcus on the spirit of reducing the number of save: calls, I'm not convinced that save: frequency is the source of this particular problem because it's only called when the pan gesture ends.
Instead, the crash-log screenshot looks very much like a KVO observer has been deallocated without first being removed as an observer.
Review your code and make sure that all addObserver:forKeyPath:context: calls are balanced by removeObserver:forKeyPath:context: and that it's not possible for your observer(s) to be deallocated without first removing their observances.
If you can't spot where it might be going wrong then perhaps edit your question to include the KVO-related code so we can take a look at it.
As for the could not fulfill a fault exception, that's a different issue and probably deserves a different stack overflow question, complete with the full stack trace.
Core Data can handle a surprising amount of data at just about any pace you want. However your code may be doing more work than is needed.
First, you appear to be saving on each move. That is completely unnecessary. Core Data works the same whether the entity is saved or not. However, saving to disk is expensive. So if you are saving on every movement/change you can be causing a problem. Removing those saves will only help. Consider saving only when the user expects a pause: leaving the app, changing views, etc.
Second, what thread is this crash coming from? If you have a multi-threaded situation here and you are receiving notifications on the wrong thread you can get some odd crashes. The UI expects/demands that you interact with it only on the main thread (thread 0). Core Data has some rules around threading as well. I cannot tell from your snippet of the stack trace if this is an issue. Posting more of the stack trace will help.
Update
Saving that often hurts the user experience because you are blocking the main thread on each save. While it should not directly cause a crash it could in theory cause notifications to get "queued up" waiting for the writes to be done. Better to save every X seconds or something than block on each notification firing.
Can you post the full stack trace? The snippet you showed has removed a great deal of information that will help to diagnose the problem.
As for not duplicating, that tends to also point towards a threading issue. Not every CPU is identical. A threading issue can show up on a small subset of devices and not duplicatable at all on others. I once had a threading issue show up on a single device.
Update
Further reflection. Wiring up User Events to saving in Core Data is just bad imho. There is no guarantee that the user is going to do anything sane. You would be far better off having the user event kick off a timer. If the timer is already present then kick it out a bit further. When the timer fires, you save.
That will give you a bit of disconnect and protect you from user behavior.
Answered
After a lot of digging and fixing of other bugs, I think I've figured out the problem. I haven't fixed it yet, but I'm able to repeat crashing behavior, so it's only a matter of how many hours it'll take to sort it out.
The problem lies in an ill-conceived undo grouping, which un-does part of an action but not all of it, and when that grouping is undone, a number of different crashes can occur, depending on what action is performed next. Basically, I'm left with a faulted NSManagedObject subclass instance that doesn't exist anymore, and the view controller that reacts to these other actions tries to call it from various places. When that happens, the app obviously crashes, but since none of the actions in the stack trace itself indicate the actual cause of the problem, it was hard to track down.
So this has nothing to do with save frequency, which is good. I'm still curious to see if it prevents the Core Data crashes that I believe were occurring on another thread.
Thanks to everyone who answered!
UPDATE
The actual problem was elsewhere. See my other question here to see the answer and the solution:
make NSUndoManager ignore a property of an NSManagedObject
Have been working on a Tower Defense game for iOS for some time now. I am using Cocos2d (v0.99.5 not to sure). Recently started to get into some problems when I started remove sprites.
Have profiles using instruments and Zombies and here's what I get:
The code for updating the sprites (and also delete them) is located in update:
-(void) update:(ccTime)deltaTime {
if (paused)
return;
CCArray *childs = [textureAtlas children];
//Update all objects
for (GameSprite *tempChar in childs) {
if ([tempChar respondsToSelector:#selector(updateStateWithDeltaTime:andListOfGameObjects:andAtlas:)]) {
[(GameSprite*)tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:[textureAtlas children] andAtlas:textureAtlas];
}
}
//Remove dead projectiles
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Projectile.class]) { //(**)
if (![tempChar alive]) {
[tempChar remove];
}
}
}
//Remove dead towers
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Tower.class]) {
if (![tempChar alive]) {
[tempChar remove];
}
}
}
//Remove dead creeps
for (GameSprite *tempChar in childs) {
if ([tempChar isKindOfClass:Creep.class]) {
if (![tempChar alive]) {
[tempChar remove];
}
}
}
}
The remove method in GameSprite.m :
-(void)remove {
CCLOG(#"remove");
[self removeFromParentAndCleanup:YES];
}
The first block updates the sprites in the SpriteBatchNode/textureAtlas. The three remaining blocks checks the different objects if the should be deleted. (their alive-variable set to NO?). The reason I have three blocks of different types is due to the fact that projectiles have (weak) reference to creep they are shooting at and needs to be deleted before the creep.
So my problem is that it randomly crashes when a projectile hits a creep and the projetile (and all other projectiles shooting at that creep) are to be removed). The creeps reference count gets down to 0 but seems to still be in the childs array. Cause the error I get is:
*** -[NormalProjectile isKindOfClass:]: message sent to deallocated instance 0xff33e30
on the row I marked with (**)
Either the problem is with me understanding Cocos2d's removeFromParentAndCleanUP: or that my "solution" for handling the projectile-creep-relationship is bad from a memory point of view. Any ideas on how to solve this or debug it further?
Your fast enumeration techniques for going through the array are casting all your sprites in that array as a custom subclass of CCSprite, then checking to see if they are instead a different custom subclass, like Tower, etc. I don't see this as healthy programming practice, since methods in one class may not be in the other, yet you are casting them nonetheless.
That may or may not be contributing to your issue, but a wiser approach is to keep an assign iVar CCArray for children of a specific class. i.e. put all your projectiles into one array at the time they are created and added to the batch node then you can iterate through all projectiles, towers, etc by going through those individual arrays, where you know what kind of class they already are. Then remove them from the array as well as from the parent when necessary. I would think this is quite a bit safer than going through all the game's sprites in their entirety that are in your batchnode, which may include spites not being used, perhaps, and casting them and testing them as different classes.
I have an iPad app that I am testing in Instruments before beta testing. I have gotten rid of all memory leaks except one, and I can't find any information on it. I am baffled as to what to do, since my code never mentions the leaking object which is an instance of NSDecimalNumberPlaceHolder.
For sure I am using NSDecimalNumber. I create 2 decimals per user operation and each I time I run a cycle of the app (which performs some math operation on the two NSDecimalNumbers) I generate four instances of this NSDecimalPlaceHolder thing. Since I do not know how it gets created, I do not know how to release or dealloc it so as to not generate these 16 btye leaks over and over again.
Is it possible that these are not really leaks?
I have run the XCode Analyzer and it reports no issues.
What I'm doing is this:
I send a decimal number from my controller over to my model (analyzer_) which performs the operations and sends back the result.
[[self analyzer_] setOperand:[NSDecimalNumber decimalNumberWithString:anotherStringValue]];
The setOperand method looks like this:
-(void)setOperand:(NSDecimalNumber*)theOperand
{
NSLog(#"setOperand called");
operand_ = theOperand;
//[operand_ retain];
}
Note that if I don't retain operand_ "somewhere" I get a BAD_ACCESS crash. I currently retain and release it later where the operand and the previously provided operand (queuedOperand_) are operated upon. For example:
{
[self performQueuedOperation];
queuedOperation_ = operation;
queuedOperand_ = operand_;
}
return operand_;
[operand_ release];
where performQueuedOperation is:
-(void)performQueuedOperation
{
[operand_ retain];
if ([#"+" isEqualToString:queuedOperation_])
{
#try
{
operand_ = [queuedOperand_ decimalNumberByAdding:operand_];
}
#catch (NSException *NSDecimalNumberOverFlowException)
{
//viewController will send decimal point error message
}
<etc for the other operations>
}
Let me know if this is not clear. Thanks.
Try Heapshot in Instruments, see: When is a Leak not a Leak?
If there is still a pointer to the memory that is no longer used it is not a leak but it is lost memory. I use Heapshot often, it really works great. Also turn on recording reference counting in the Allocations tool and drill down. Here is a screenshot: