Objective-C - Replicating exit(0) and fixing EXC_BAD_ACCESS - objective-c

Having some seriously frustrating issues with my latest app!!
I'm using OpenGLES 1 to draw models to the screen that are constantly updating, then you can take a screenshot and share however you like, after that you can return to the app start screen and start again.
All seems to work fine the first time the app runs through, no crashes. But if you run through it once then try to go through again, as soon as the OpenGLES drawing view has to start drawing the elements, it throws up an EXC_BAD_ACCESS error on my glDrawElements line.
I given up on trying to fix this error for now, I've had no luck in the last 2 weeks!!
I have set the property in my plist so that the app doesn't run in the background, and whenever I run it through once, then exit it and load it up again it will run through again absolutely fine.
So what I was wondering is if I could somehow emulate the functionality of the exit(0) feature, but without actually exiting from the app?
So in essence, each time the user navigates from the sharing page back to the home page, the app would run the exit code, leaving the app to run from scratch again.
This would, in my head at least, not be a great way at all of dealing with the situation, but I need results quick and this seems like a good way out for now until I can get to the bottom of the actual error.
Thanks,
Matt
EDIT 1: I'm not looking to put the app on the store with this fix in place, it is just a temporary thing.
EDIT 2: Posted code containing my draw functionality, taken and adapted from Bill Dudney's WaveFront .obj loader
for(WaveFrontOBJGroup *group in scene.groups)
{
NSString *matPath = [NSString stringWithFormat:#"%#%#.mtl", materialName, modelPart];
NSArray *materialArray = [WaveFrontOBJMaterial materialsFromLibraryFile:matPath];
WaveFrontOBJMaterial *mat = [materialArray objectAtIndex:0];
if(group.smoothing)
glShadeModel(GL_SMOOTH);
else
glShadeModel(GL_FLAT);
// load (if necessary) and bind the vertices
GLuint verticesName = [group verticesName:GL_STATIC_DRAW];
glBindBuffer(GL_ARRAY_BUFFER, verticesName);
glVertexPointer(3, GL_FLOAT, 0, 0);
// load (if necessary) and bind the normals
GLuint normalsName = [group normalsName:GL_STATIC_DRAW];
glBindBuffer(GL_ARRAY_BUFFER, normalsName);
glNormalPointer(GL_FLOAT, 0, 0);
ColorRGBA color = mat.ambientColor;
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat *)&color);
color = mat.diffuseColor;
if(alpha)
glColor4f(color.red, color.green, color.blue, mat.disolve);
else
glColor4f(color.red, color.green, color.blue, color.alpha);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat *)&color);
color = mat.specularColor;
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *)&color);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mat.shine);
// load (if necessary) and bind the texture
if(group.textureCoordinatesIndexData.length > 0)
{
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
GLuint textureCoordsName = [group textureCoordinatesName:GL_STATIC_DRAW];
glEnable(GL_TEXTURE_2D);
glBindBuffer(GL_ARRAY_BUFFER, textureCoordsName);
glTexCoordPointer([group texCoordSize], GL_FLOAT, 0, 0);
GLuint texId = [mat.diffuseTexture textureName];
glBindTexture(GL_TEXTURE_2D, texId);
}
GLuint indexesName = [group indexesName:GL_STATIC_DRAW];
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexesName);
glDrawElements(GL_TRIANGLES, group.indexCount, GL_UNSIGNED_SHORT, NULL);
//^^This line above is where I get the "EXC_BAD_ACCESS" message
if(group.textureCoordinatesIndexData.length > 0)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
I've checked group.indexCount through breakpoints and NSLog's and it is not null, it is exactly the same as when it runs through the first time which works fine.
I realise it won't necessarily be on that line, I've heard other talk about it being due to variables not being initialised as pointers - * - or NSStrings not having matching identifiers and values (#"%# sometext %#", variable); but I don't see any of those in my code, of course I could easily be missing one.

Remember two things:
1) You have a method -(void)viewWillAppear:(BOOL)animated in your home ViewController. This will call always (!) when user return to this screen (not like viewDidLoad).
2) If you even do this like you described you probably didn't get to the AppStore!

I'm not sure why your application won't compile on the simulator, but you should try to link your libraries appropriately so that you can get that up and running - nothing is better for bad access errors than zombies.
If you know your error is from calling glDrawElements then you should look into what's being passed to it. This link might be able to help you.
The reason you can't get the "functionality" of exiting your app while still in the app is because it's not a function of the application - it's the OS throwing away everything associated with the application and then reloading it. By definition you won't be able to do this without actually shutting down.
However, if your problem is fixed by this, then it is likely that there's something in your setup that needs to be redone (initializations, declarations, etc). Check to see what's getting set to nil or thrown away and then set it back up.
Without looking at your code I can't say this with any certainty, but you may be able to write a method that overwrites/reinitializes all of your current variables. This would probably replicate the part of exiting the application that you need.

Related

Can't fade out with FadeInOutSampleProvider

I'm trying to use NAudio's FadeInOutSampleProvider to fade in a sample and fade it out. The fade in works OK, but instead of fading out gradually I get abrupt silence from where the fade-out should begin.
What's the correct way to fade out with FadeInOutSampleProvider?
Here's how I'm trying to do it:
IWaveProvider waveSource; // initialised by reading a WAV file
// The ISampleProvider will be the underlying source for the following operations
ISampleProvider sampleSource = waveSource.ToSampleProvider();
// Create a provider which defines the samples we want to fade in
// (including the full-volume "middle" of the final output)
ISampleProvider fadeInSource = new OffsetSampleProvider(sampleSource);
fadeInSource.TakeSamples = most_of_file; // calculation omitted for brevity
// Create a provider which defines the samples we want to fade out:
// We will play these samples when fadeInSource is finished
ISampleProvider fadeOutSource = new OffsetSampleProvider(sampleSource);
fadeOutSource.SkipOverSamples = fadeInSource.TakeSamples;
// Wrap the truncated sources in FadeInOutSampleProviders
var fadeIn = new FadeInOutSampleProvider(fadeInSource);
fadeIn.BeginFadeIn(500); // half-second fade
var fadeOut = new FadeInOutSampleProvider(fadeOutSource);
fadeOut.BeginFadeOut(500);
// doc-comments suggest the fade-out will begin "after first Read"
I'm expecting fadeOut to initially read non-zero samples from 500ms before the end of the original source, but fade out to zeros by the end of the source.
However, when I play fadeIn to completion, then play fadeOut, I find that the very first Read call to fadeOut fills the buffer with zeros.
Am I doing something wrong? Or is there a bug in NAudio?
Note: I'm handling the sequential playback using a ConcatenatingSampleProvider which I implemented myself — I can't anything similar in NAudio's API. It's pretty trivial, so I've omitted the source here.
The problem is you're trying to reuse sampleSource twice in your graph. So sampleSource has already been read to the end before anything is read from fadeOutSource. Probably for your usage, it would be better for FadeInOutSampleProvider to be able to "schedule" a fade-out after a known number of samples.
An alternative approach is a FadeOutSampleProvider that caches the fade-out duration, and then when it detects the end of its source has been reached, it returns the cached portion faded out. It does mean latency is introduced.

How can I make an SLTextField tappable?

I have been writing a test with "Subliminal's" "SLTextField" and have run into some really annoying issues lately.
I have written a test which should update a text field, clear it, and update it again.
For some reason when I use "SLTextField" to change the element text I consistently receive errors stating that "SLTextFields" aren't tappable elements. Is this true? If so, what is the point of having an "SLTextField" class at all?
I can rewrite the same test code to find the element as an "SLElement", at which point I can tap the element, open up a keyboard object and type the necessary text, but it seems like I'm circumventing the entire functionality of Subliminal in doing this.
What's wrong w/"SLTextField"?
Code:
SLTextField *textField = [SLTextField elementWithAccessibilityLabel:fieldName];
SLWaitUntilTrue([textField isTappable], DEFAULT_TIMEOUT);
textField.text = newValue;
The above code throws an error, stating that "textField" never becomes tappable. Alternatively, the code below works perfectly, though it's unnecessarily verbose and seems to make "SLTextField" superfluous.
Code:
SLElement *field = [SLElement elementWithAccessibilityLabel:fieldName];
[field tapAtActivationPoint];
//fill with text
SLKeyboard *kb = [SLKeyboard keyboard];
SLKeyboardKey *deleteKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Delete"];
while(![field.value isEqualToString:#""]){
[deleteKey touchAndHoldWithDuration:1.2];
}
[kb typeString:newValue];
SLKeyboardKey *doneKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Next"];
if(![doneKey isValid]){
doneKey = [SLKeyboardKey elementWithAccessibilityLabel:#"Done"];
}
[doneKey tap];
[kb hide];
Is your TextField within a TableViewCell? And are you seeing this on iOS 7?
If so, it might have been fixed by https://github.com/inkling/Subliminal/pull/202 (merged 6/6/2014).

Frame Listener in QMLOgre Lib Freeze Window

I'm newbie in using ogre3D and I need help on a certain point!
I'm trying a library mixing ogre3D engine and qtml :
http://advancingusability.wordpress.com/2013/08/14/qmlogre-is-now-a-library/
this library works fine when you want to draw some object and rotate or translate these objects already initialise in a first step.
void initialize(){
// we only want to initialize once
disconnect(this, &ExampleApp::beforeRendering, this, &ExampleApp::initializeOgre);
// start up Ogre
m_ogreEngine = new OgreEngine(this);
m_root = m_ogreEngine->startEngine();
m_ogreEngine->setupResources();
m_ogreEngine->activateOgreContext();
//draw a small cube
new DebugDrawer(m_sceneManager, 0.5f);
DrawCube(100,100,100);
DebugDrawer::getSingleton().build();
m_ogreEngine->doneOgreContext();
emit(ogreInitialized());
}
but If you want to draw or change the scene after this initialisation step it is problematic!
In fact in Ogre3D only (without the qtogre library), you have to use a frameListener
which will connect the rendering thread and allow a repaint of your scene.
But here, we have two ContextOpengl: one for qt and the other one for Ogre.
So If you try to put the common part of code :
createScene();
createFrameListener();
// La Boucle de rendu
m_root->startRendering();
//createScene();
while(true)
{
Ogre::WindowEventUtilities::messagePump();
if(pRenderWindow->isClosed())
std::cout<<"pRenderWindow close"<<std::endl;
if(!m_root->renderOneFrame())
std::cout<<"root renderOneFrame"<<std::endl;
}
the app will freeze! I know that startRendering is a render loop itself, so the loop below never gets executed.
But I don't know where to put those line or how to correct this part!
I've also try to add a background buffer and to swap them :
void OgreEngine::updateOgreContext()
{
glPopAttrib();
glPopClientAttrib();
m_qtContext->functions()->glUseProgram(0);
m_qtContext->doneCurrent();
delete m_qtContext;
m_BackgroundContext= QOpenGLContext::currentContext();
// create a new shared OpenGL context to be used exclusively by Ogre
m_BackgroundContext = new QOpenGLContext();
m_BackgroundContext->setFormat(m_quickWindow->requestedFormat());
m_BackgroundContext->setShareContext(m_qtContext);
m_BackgroundContext->create();
m_BackgroundContext->swapBuffers(m_quickWindow);
//m_ogreContext->makeCurrent(m_quickWindow);
}
but i've also the same error:
OGRE EXCEPTION(7:InternalErrorException): Cannot create GL vertex buffer in GLHardwareVertexBuffer::GLHardwareVertexBuffer at Bureau/bibliotheques/ogre_src_v1-8-1/RenderSystems/GL/src/OgreGLHardwareVertexBuffer.cpp (line 46)
I'm very stuck!
I don't know what to do?
Thanks!

Fastest way to resize (zoom) a group of sprites in cocos2d

I wanna to zoom a group of about 10 sprites at the same time. The sprites are different sprite layers with transparent background.
I'm trying to preattach all the sprite at first to the layer and store the reference in an array. After that as I click the button I do this:
Sorry this is Javascript but in Objective-C it's almost the same.
attr.zoomAllVisibleSprites = function() {
for (var i = 0; i < this.SpriteArray.length; i++) {
if (this.SpriteArray[i].isVisible()) {
this.SpriteArray[i].setAnchorPoint(cc.PointMake(0.5,0.5));
this.SpriteArray[i].setScale(2, 2);
}
}
}
The execution of this little snippet requires on my Android phone about 2-3 seconds which is too much for my game. Is there a way to do it faster, optimize this code. Maybe group in a different way the sprites could help ?
You could replace your array of sprites by using a for-loop for all CCSprites, like this:
for(CCSprite *sprite in self.view.subviews){ //or scenes or whatever
if([sprite isMemberOfClass:[CCSprite class]]){
//Do your thing }}
Might not help alot, but you can atleast ditch the array ;D
It might be the case that device runs out of memory, try switching to 16-bit textures, it can be done by adding this line on start:
CCTexture2D::setDefaultAlphaPixelFormat(kCCTexture2DPixelFormat_RGBA4444);
Also you can unload unneeded textures before load new scene, like this:
CCSpriteFrameCache::sharedSpriteFrameCache()->removeUnusedSpriteFrames();
CCTextureCache::sharedTextureCache()->removeUnusedTextures();
CCDirector::sharedDirector()->purgeCachedData();
If that doesn't work then it might be a issue with Open-GL, in that case you should have a look at this. It worked like a magic trick for me.
All the Best.. :)

UIScrollView lazy loading of images to reduce memory usage and avoid crash

My app, using scrollview that loads multiple images with NSOperation (Max around 100sh). I tried to test it out on my ipod 2Gen and it crashes due to low memory on device, but works fine on ipod 4th Gen. On 2nd Gen, it crashes when it loads about 15-20 images. How should I handle this problem ?
You could load you images lazily. That means, e.g., just a couple of images at a time in your scroll view, so that you can animate to the next and the previous one; when you move to the right, e.g., you also load one more image; at the same time, you unload images that are not directly accessible anymore (e.g. those that have remained to the left).
You should make the number of preloaded image sufficiently high so that the user can scroll without waiting at any time; this also depends on how big those images are and where they come from (i.e., how long it takes to load them)... a good starting point would be, IMO, 5 images loaded at any time.
Here you will find a nice step by step tutorial.
EDIT:
Since the link above seems to be broken, here is the final code from that post:
-(void)scrollViewDidScroll:(UIScrollView *)myScrollView {
/**
* calculate the current page that is shown
* you can also use myScrollview.frame.size.height if your image is the exact size of your scrollview
*/
int currentPage = (myScrollView.contentOffset.y / currentImageSize.height);
// display the image and maybe +/-1 for a smoother scrolling
// but be sure to check if the image already exists, you can do this very easily using tags
if ( [myScrollView viewWithTag:(currentPage +1)] ) {
return;
}
else {
// view is missing, create it and set its tag to currentPage+1
}
/**
* using your paging numbers as tag, you can also clean the UIScrollView
* from no longer needed views to get your memory back
* remove all image views except -1 and +1 of the currently drawn page
*/
for ( int i = 0; i < currentPages; i++ ) {
if ( (i < (currentPage-1) || i > (currentPage+1)) && [myScrollView viewWithTag:(i+1)] ) {
[[myScrollView viewWithTag:(i+1)] removeFromSuperview];
}
}
}