SceneKit physics contact method crashes with EXC_BAD_ACCESS - ios

In a SceneKit project, the following method is intermittently (but consistently) crashing with EXC_BAD_ACCESS. Specifically, it says Thread 1: EXC_BAD_ACCESS (code=1, address=0x0).
contactTestBetween(_:_:options:)
The method is called from inside SceneKit's SCNSceneRendererDelegate method. It's also being run on the main thread because otherwise, this code crashes even more often. So, here's the greater context:
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
var ball = self.scene?.rootNode.childNode(withName: "ball", recursively: true)
var ballToFloorContact: [SCNPhysicsContact]?
let theNodes: [SCNNode]? = self.scene?.rootNode.childNodes.filter({ $0.name?.contains("floor") == true})
let optionalWorld: SCNPhysicsWorld? = self.scene?.physicsWorld
DispatchQueue.main.async {
if let allNodes = theNodes {
for i in 0..<allNodes.count {
let n = allNodes[i]
if let b = n.physicsBody, let s = ball?.physicsBody {
ballToFloorContact = optionalWorld?.contactTestBetween(b, s)
}
}
}
}
}
The SCNSceneRendererDelegate is set in viewDidLoad:
scnView.delegate = scnView
Additional info:
When the crash occurs, optionalWorld, b, and s are all properly defined.
I originally had the call to filter located inside the DispatchQueue, but it was causing a crash that seemed identical to this one. Moving that line outside the DispatchQueue solved that problem.
Question: Any idea what might be causing this crash, and how I might avoid it? Am I doing something wrong, here?
Thanks!
UPDATE: I tried adding the following guard statement to protect against a situation where the contactTestBetween method is, itself, nil (after all, that seems to be what Xcode is telling me):
guard let optionalContactMethod = optionalWorld?.contactTestBetween else {
return
}
However, after some additional testing time, contactTestBetween eventually crashed once again with EXC_BAD_ACCESS on the line guard let optionalContactMethod = optionalWorld?.contactTestBetween else {. I truly do not understand how that could be, but it be. Note that I tried this guard paradigm both with and without the presence of the DispatchQueue.main.async call, with the same result.

I did two things, here:
I added Accelerometer and Gyroscope to the UIRequiredDeviceCapabilities key in my Info.plist file. I did this because my game uses Core Motion, but I had neglected to include the necessary values.
On a hunch, I replaced the SCNSceneRendererDelegate method renderer(_: SCNSceneRenderer, updateAtTime: TimeInterval) with the alternative method renderer(_: SCNSceneRenderer, didRenderScene: SCNScene, atTime: TimeInterval).
Since doing these things, I haven't been able to reproduce a crash.

An alternative unsafe fix is
try! await Task.sleep(nanoseconds: 1)
somewhere before invoking contactTestBetween().
This answer sits here as a warning to anyone who might want to use async/await as a fix or is inadvertendly using some await (any kind of await works) to break out of the run loop which somehow makes debugging EXC_BAD_ACCESS nearly impossible because it will get triggered rarely enough.
I know from experience that if the above fixed a race then races WILL still happen especially and mostly with CPUs running at 100% and actually I got contactTestBetween to crash once after many tries.
I have no clue what's going on behind the scenes or how to synchronize with physicsWorld.
#West1 solution is still the best if it works as advertised.

Related

Re-Assigning instance of AVAudioPlayer in iOS13 leads to BAD_ACCESS runtime error

I have an app, that is for many years in the AppStore and runs without any crashes (last deployment target iOS 12.4). I have some code for playing a sound on certain events in the app.
Now I tried to upgrade my app for iOS 13 and without changing any code related to that “playSound” thing, I always get this runtime error, when testing on a real device. Does not happen on simulator.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x48)
PLEASE: Before you mark that question as “duplicate”, consider that this must have something to do with the release of iOS13, because before it didn’t happen and the code is just "usual".
Here is my code, also on gitHub.
I have a property in my ViewController to prevent ARC deallocating my AVAudioPlayer:
private var mySoundPlayer: AVAudioPlayer = AVAudioPlayer()
I have a routine, where the “play sound” should be performed (here happens the error, when assigning a new instance of AVAudioPlayer. The resourceURL is not the problem, the RE-ASSIGNING is the problem, I tested it with a new instance not being assigned and i did not crash.
// -------------------------------------------------
// MARK: Private Methods
// -------------------------------------------------
private func makeSoundEvent(_ soundEvent : SoundEvent) {
guard Settings().getSound() == .soundON else { return }
guard let urlToRessource : URL = soundEvent.getFileURLToSoundRessource() else { return }
do {
mySoundPlayer = try AVAudioPlayer(contentsOf: urlToRessource)
try? AVAudioSession.sharedInstance().setActive(true)
mySoundPlayer.play()
}
catch { print("Could not create AVPlayer for ressource \(urlToRessource)") }
}
And here I have the call of that subroutine, somewhere in my viewDidLoad() with a delay of 2 seconds.
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
self.makeSoundEvent(.startFanfare)
}
I think it somehow does not work because of the async thread. But since it is on the main thread, I thought this should work.
Just remove the initialisation and it will work
private var mySoundPlayer: AVAudioPlayer!
Cheers!!!
private var mySoundPlayer: AVAudioPlayer = AVAudioPlayer()
AVAudioPlayer doesn't have init() listed as a valid initializer. The reference page shows four separate initializers, all of which take at least one parameter. If you were using the line above in code prior to iOS 13.1 without crashing, you were initializing the audio player incorrectly and probably just getting lucky that it wasn't a problem.
I don't know what changed specifically with AVAudioPlayer in iOS 13.1, but the release notes show quite a few audio-related changes, and it seems likely that some change to that class introduced a dependency on something that happens at initialization time.

Thread 1: EXC_BAD_ACCESS (code=1, address=0x8000000000000010)

I have an application which has a collection view full of videos and images. When you tap on one of them you segue to a full screen to view the media. When I tap a image It works as needed. But when I run my application and tap one of those videos I get the error shown bellow:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x8000000000000010)
I get this here:
class AppDelegate: UIResponder, UIApplicationDelegate {
Is this possibly due to memory problems?
What is happening and how do i fix this?
I have looked here but was unable to solve it.
I have found that the fail actually occurs when the bellow function is called.
func getAndShowMedia(post: Post) {
if post.media[numberMedia].image != nil {//here is still works, I am assuming this line is the line which actualy fails
print("imageooooo")//When setting break point here it will crash
mediaView.image = nil
mediaView.image = (post.media[numberMedia].image)!
} else {
mediaView.layer.removeFromSuperlayer()
let videoURL = post.media[numberMedia].videoURL
let player = AVPlayer(url: videoURL! as URL)
let playerLayer = AVPlayerLayer(player: player)
print("videooooooo")
playerLayer.frame = mediaView.bounds
mediaView.layer.addSublayer(playerLayer)
player.play()
}
}
Here:
func loadOtherData() {
getAndShowMedia(post: selectedPost!)
}
Please check your code and tell me where be crash.
I read your code and assume happening crash, i write be below point and check the debug mode.
post.media[numberMedia].image you check is not nil please there case use if let statement.
post.media[numberMedia].videoURL is geting in your model URL object that will happening crash.
mediaView.layer.removeFromSuperlayer() i don't understand this code you have remove layer but you add sublayer mediaView.layer.addSublayer(playerLayer) then remove sublayer. Check your sublayer is available then remove sublayer, not remove mediaView layer.
use this code remove layer.
mediaView.layer.sublayers?.forEach({ $0.removeFromSuperlayer() })
I hope this code will be work.
From the Breakpoint menu in Xcode, press the '+' button at the bottom left then add an exception breakpoint. This will show you exactly where your app is crashing and you'll be able to debug it properly.
You have a few forced unwraps there that may be nil, you should avoid that.

NSOperation operationDidFinish with errors, add operation to queue again

I'm subclassing a GroupOperation and would like to run it again if it fails and the amount of attempts is less than 5. I also have a delayOperation to delay it by 3 seconds. I can't get this to work however, this is what I have so far:
self.conversation is an Object.
produceConversationOperation() returns a backgroundOperation
override func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) {
if let _ = errors.first {
if let _ = operation as? BackgroundOperation {
context.performBlockAndWait({ () -> Void in
self.conversation.retryCountValue++
if self.conversation.retryCountValue < 5 {
let postConversationOperation = self.produceConversationOperation()
let delayOperation = DelayOperation(interval: 3)
postConversationOperation.addDependency(delayOperation)
self.produceOperation(delayOperation)
self.produceOperation(postConversationOperation)
}
else {
self.conversation.retryCountValue = 0
}
self.saveContext()
})
}
}
}
For some reason the operation isn't running again after the first failure. I have a feeling the issue is with the self.produceOperation method but I have no idea what.
There have been a few solutions to similar questions but I haven't found anything that has helped.
Thanks
Bit late to answer this, but you might want to check out using RetryOperation from http://github.com/danthorpe/Operations which supports many of these issues:
max count of retries
customisable delay (with a wait strategy if you need random/exponential/etc)
it accepts a GeneratorType of the operation
it accepts a "retry handler" trailing closure which allows the consumer to "tweak" the next instance of the operation which will be retried.
To answer this question a bit more concretely though, and without knowing for sure what produceOperation() is doing, I would hazard a guess that it is not actually creating a fresh new instance, but in some way is returning the original operation.

Ios popToRootViewControllerAnimated causing Memory Issues

I am making my SpriteKit game. When the player dies, my goal is to have the game transition back to the start screen. This is accomplished by the code below. However, I notice that the memory increases each time a new game begins. Xcode Instruments is not showing a memory leak. When the memory reaches roughly 150mb the games frame rate drops and the game become unplayable.
In the GameScene I call this function when the player dies
func gameOver(){
if let block = gameOverBlock {
worldNode.removeAllChildren()
worldNode.removeAllActions()
worldNode.removeFromParent()
self.removeAllChildren()
block()
}
}
Back in the GameViewController the following functions get called
scene!.gameOverBlock = {
[weak self] in
self!.goBack()
}
}
func goBack(){
scene!.removeFromParent()
navigationController!.popToRootViewControllerAnimated(false)
return
}
If anyone has any ideas as to how I can accomplish this without a memory leak, it would much be appreciated.
After commenting out tons of code, I have found the problem. The methods that I have posted above were not causing the leak, as Matthew suggested, there was a strong reference in the middle of my code that was stopping the ARC from releasing memory. Ill post the problem code incase anyone else may have a similar problem.
In my GameViewController, I had the following block:
scene!.zoomInBlock = {
self.scene!.size = CGSizeMake(self.scene!.size.width / 2, self.scene!.size.height / 2)
}
The correct way (without causing a strong reference) to write this would be:
scene!.zoomInBlock = {
[unowned self] in self.scene!.size = CGSizeMake(self.scene!.size.width / 2, self.scene!.size.height / 2)
}

My iOS app freezes but no error appears

Does any body know what I need to check if app freezes after some time? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread? is there any way to identify?
Launch your app and wait for it to freeze. Then press the "pause" button in Xcode. The left pane should show you what method is currently running.
Generally, it is highly recommended to perform on the main thread all animations method and interface manipulation, and to put in background tasks like download data from your server, etc...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to perform in background
dispatch_async(dispatch_get_main_queue(), ^{
//call back to main queue to update user interface
});
});
Source : http://www.raywenderlich.com/31166/25-ios-app-performance-tips-tricks
Set a break point from where the freeze occurs and find which line cause that.
Chances may be,Loading of large data,disable the controls,overload in main thread,Just find out where that occurs using breakpoints and rectify based on that.
I believe it should be possible to periodically check to see if the main thread is blocked or frozen. You could create an object to do this like so:
final class FreezeObserver {
private let frequencySeconds: Double = 10
private let acceptableFreezeLength: Double = 0.5
func start() {
DispatchQueue.global(qos: .background).async {
let timer = Timer(timeInterval: self.frequencySeconds, repeats: true) { _ in
var isFrozen = true
DispatchQueue.main.async {
isFrozen = false
}
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + self.acceptableFreezeLength) {
guard isFrozen else { return }
print("your app is frozen, so crash or whatever")
}
}
let runLoop = RunLoop.current
runLoop.add(timer, forMode: .default)
runLoop.run()
}
}
}
Update October 2021:
Sentry now offers freeze observation, if you don't wanna roll this yourself.
I reached an error similar to this, but it was for different reasons. I had a button that performed a segue to another ViewController that contained a TableView, but it looked like the application froze whenever the segue was performed.
My issue was that I was infinitely calling reloadData() due to a couple of didSet observers in one of my variables. Once I relocated this call elsewhere, the issue was fixed.
Most Of the Time this happened to me when a design change is being called for INFINITE time. Which function can do that? well it is this one:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
Solution is to add condition where the function inside of viewDidLayoutSubviews get calls only 1 time.
It could be that another view is not properly dismissed and it's blocking user interaction! Check the UI Debugger, and look at the top layer, to see if there is any strange thing there.

Resources