I have a problem in objective-c with a for loop. I want to wait the end of the loop before printing the elements on screen
for (NSDictionary *item in allItems) {
[self print:item];
};
when the loop ends, everything is print, but I would like printing during the loop.
how can i resolve it?
i tried with blocks without success
the print method:
-(void)print:(NSDictionary*)item{
NSString *message=[item objectForKey:#"message"];
if(message){
int userid = 0;
NSString *nome=[item objectForKey:#"name"];
NSString *sesso=[item objectForKey:#"sex"];
NSString *foto=[item objectForKey:#"photo"];
.........
It's not clear what your -print: method does, but I assume that it draws something on screen. Drawing in iOS and MacOS X doesn't happen immediately -- you draw into a graphics context that's managed by the graphics system, and that context is composed with others to generate the image that's ultimately drawn on the screen.
In other words, the screen drawing doesn't happen when you execute drawing commands, it happens when the graphics system periodically updates the screen. This is normal behavior -- it's not a problem.
Related
I know very little about using background threads, but this seems to play my sound in the way that I need it to as follows:
1) I need this very short sound effect to play repeatedly even if the sound overlaps.
2) I need the sound to be played perfectly on time.
3) I need the loading of the sound to not affect the on-screen graphics by stuttering.
I am currently just trying out this method with one sound, but if successful, I will roll it out to other sound effects that need the same treatment. My question is this: Am I using the background thread properly? Will there be any sort of memory leaks?
Here's the code:
-(void) playAudio {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSString *path = [NSString stringWithFormat:#"%#/metronome.mp3", [[NSBundle mainBundle] resourcePath]];
NSURL *metronomeSound = [NSURL fileURLWithPath:path];
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:metronomeSound error:nil];
[_audioPlayer prepareToPlay];
[_audioPlayer play];
});
}
//handles collision detection
-(void) didBeginContact:(SKPhysicsContact *)contact {
uint32_t categoryA = contact.bodyA.categoryBitMask;
uint32_t categoryB = contact.bodyB.categoryBitMask;
if (categoryA == kLineCategory || categoryB == kLineCategory) {
NSLog(#"line contact");
[self playAudio];
}
}
I use the AVAudioPlayer and use it asynchronously and in background threads without any problems and no leaks as far as I can tell. However, I have implemented a singleton class that handles all the allocations and keeps an array of AVAudioPlayer instances that also play asynchronously as needed. If you need to play a sound repeatedly, you should allocate an AVAudioPlayer instance for every time you want to play it. In that case, latency will be negligible and you can even play the same sound simultaneously.
Concerning your strategy I think it needs some refinements, in particular if you want to prevent any delays. The main problem is always reading from disk, which is the slowest operation of all and your limiting step.
Thus, I would also implement an array of AVAudioPlayers each already initialized to play a specific sound, in particular if this sound is played often and repeatedly. You could remove those instances of players that are played less often from the array if memory starts to grow and reload them a few seconds before if you can tell which ones will be needed.
And one more thing... Don't forget to lock and unlock the array, if you are going to access it from multiple threads or better yet, create a GCD queue to handle all accesses to the array.
I have an NSDragOperation that gets a property lists path upon the user dragging it into the window. That seems to work just fine, and I can save the path information to an NSString:
NString *thisPath = draggedFilePath;
NSLog(#" %#",thisPath);
output: 2014-02-09 09:19:46.072 app[5944:303] /Users/Me/Desktop/file.plist
The problem starts when I go into a dispatch queue. When I try and read the NSString from inside the background queue the output becomes NSPrincipalClass. Does anyone know why this is happening, or if I'm supposed to convert the NSString to some other format before entering dispatch_queue_t?
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
for (NSUInteger i = 0; i < 1; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
});
}
NSLog(#" %#",thisPath);
output: 2014-02-09 09:19:56.234 app[5944:12203] NSPrincipalClass
EDIT: the output of the string using NSLog inside dispatch queue turns up random symbols, and also crashes. The last output was <__NSMallocBlock__: 0x55a860> - I have other strings that seem to be just fine within the same function, so I really don't know what is causing this.
one of three things is happening...
it is being deallocated, and a new object is being allocated in its place... you can try zombies...
it is getting assigned to a garbage value ie. draggedFilePath isn't ever initialized to zero, and isn't set to a good value.
your stack is getting smashed and it just happens to be there when it crashes... this is the hardest to find.
you are going to have to turn on zombies, the exception breakpoint and just step through it in the debugger... if that fails you get to either run in instruments with the malloc tool or turn on malloc history logging.
Does it work if you prefix the declaration of thisPath with `__block'? Like this:
__block NString *thisPath = draggedFilePath;
i am trying to set texture with this :
[[self setSpriteByTag] setTexture:[[CCTextureCache sharedTextureCache] addImage:[NSString stringWithFormat:#"%#0001.png",face]]];
when [self setSpriteByTag] return a sprite face is an image string, and they both ok because i have logged them.
this sprite is a child of a page in ccscrolayer :
one page of the layer pages :
CCLayer *page1 = [[CCLayer alloc] init];
BACK.position=ccp(screenSize.width/2,screenSize.height/2);
[page1 addChild:BACK];
when i was trying to change BACK's image , with the command i wrote here.
what happen is that i see a white screen.
i have check that the image is in my assets .
what could be the problem ?
the facr that i change a sprite that is a child of a page ?
edit :
this is the function that gives me the sprite BACK:
-(CCSprite*)setSpriteByTag
{
int currentPage=[scroller currentScreen];
[globals sharedGlobals].currentPageG=currentPage; //move to touch ended?
currentPage=currentPage+1;
//NSLog(#"PAGE:%d",currentPage);
if(currentPage==1)
{[globals sharedGlobals].WhatFace =#"BeastFace"; return BACK;}
else if(currentPage==2)
{[globals sharedGlobals].WhatFace =#"BlueFace"; return BACK1;}
else if(currentPage==3)
It gets white only in case when image is not present in the resources.. I checked running same code.. It works fine.. If I put wrong name of the image which is not present.. It changes to white... Please check for the image
[NSString stringWithFormat:#"%#0001.png",face]
M sure problem is in the image name.. :)
[NSString stringWithFormat:#"%#0001.png",face]
Your filenames will have to have the format "xxxxx0001.png". Assuming the face variable is the string "BeastFace" then the filename to load will be "BeastFace0001.png".
So far the obvious but keep in mind that the file "Beastface0001.png" will not load on the device, and neither will "beastface0001.png" because iOS devices use a case-sensitive file system. It will work on the iOS Simulator though.
I've been developing a music player recently, I'm writing my own pickers.
I'm trying to test my code to it's limits, so I have around 1600 albums in my iPhone.
I'm using AQGridView for albums view, and since MPMediaItemArtwork is a subclass of NSObject, you need to fire up a method on it to get an image from it, and that method scales images.
Scaling for each cell uses too much CPU as you can guess, so my grid album view is laggy, despite all my effort manually driving each cell's includes.
So I thought of start scaling with GCD on app launch, then save them to file, and read that file for each cell.
But, my code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
MPMediaQuery *allAlbumsQuery = [MPMediaQuery albumsQuery];
NSArray *albumsArray = allAlbumsQuery.collections;
for (MPMediaItemCollection *collection in albumsArray) {
#autoreleasepool {
MPMediaItem *currentItem = [collection representativeItem];
MPMediaItemArtwork *artwork = [currentItem valueForProperty:MPMediaItemPropertyArtwork];
UIImage *artworkImage = [artwork imageWithSize:CGSizeMake(90, 90)];
if (artworkImage) [toBeCached addObject:artworkImage];
else [toBeCached addObject:blackImage];
NSLog(#"%#", [currentItem valueForProperty:MPMediaItemPropertyAlbumTitle]);
artworkImage = nil;
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:albumsArray] forKey:#"covers"];
});
NSLog(#"finished saving, sir");
});
in AppDelegate's application:didFinishLaunchingWithOptions: method makes my app crash, without any console log etc.
This seems to be a memory problem, so many images are kept in NSArray which is stored on RAM until saving that iOS force closes my app.
Do you have any suggestions on what to do?
Cheers
Take a look at the recently-released SYCache, which combines NSCache and on-disk caching. It's probably a bad idea to get to a memory-warning state as soon as you launch the app, but that's better than force closing.
As far as the commenter above suggested, mapped data is a technique (using mmap or its equivalent) to load data from disk as if it's all in memory at once, which could help with UIImage loading later on down the road. The inverse (with NSMutableData) is also true, that a file is able to be written to as if it's directly in RAM. As a technique, it could be useful.
I am using a pin screen for login to my app. The pin screen consists of four labels and a hidden text field. When the user enters text via the keypad, I update the labels with a symbol. This works fine, except that the last label does not get actually get updated before login begins, and remains empty while the login process is completed.
These are the relevant bits of code:
//an observer has been added elsewhere
- (void)textDidChange:(NSNotification *)notification
{
UITextField *field = [notification object];
if (field == inputField)
{
NSString *newText = field.text;
if ([newText length] <= pinLength) [self updatePINDisplay];
}
}
-(void)updatePINDisplay
{
if ([pinText length] > pinLength) return;
for (NSInteger ii = 0; ii < [pinText length]; ii++)
{
UILabel *label = [pinFields objectAtIndex:ii];
[label setText:#"x"];
}
for (NSInteger ii = [pinText length]; ii < pinLength; ii++)
{
UILabel *label = [pinFields objectAtIndex:ii];
[label setText:[NSString string]];
}
if ([pinText length] == pinLength) [self login];
}
The problem arises because [self login] launches other processes which happen before the last pin label is updated, so the login occurs while the last box is still empty.
I have worked around the problem by replacing
[self login]
with
[self performSelector:#selector(login) withObject:nil afterDelay:0.1]
but I don't like the arbitrary time delay. I was hoping that maybe there was a delegate method that I could use to launch my login code after the label has been drawn. Something like:
-(void)labelDidGetDrawn
Any other (non-hack) solution is also welcome:-)
Thanks!
Based on your description, it sounds like the problem is that the 4th item doesn't get drawn until after the [self login] finishes, which is indicative that the login procedure takes some time. In iOS, drawing doesn't happen immediately, which is why you're only getting the draw if you defer the login until after the OS has an opportunity to update the display.
You have used one reasonable solution here. Another (arguably less of a hack) is to have your -[self login] spawn the login on a separate thread, or at least using an asynchronous mechanism (such as the asynchronous modes of NSURLConnection, assuming you're making a network request). Then your main thread will quickly return control to iOS and your box will draw.
With Grand Central Dispatch, you could do most of this by having the -[self login] place the network code on a background thread, and have the background thread call back to your main thread when complete. However, this can cause some problems if you want to respond to user events during the login process.
If you can, using NSURLConnection asynchronously, and setting up the delegate to report back to you when the operation is complete is probably the best choice, as it gives you the operation to cancel the NSURLConnection during the login process if the user requests it.
How about:
[label setNeedsDisplay:YES];
if ([pinText length] == pinLength) [self login];
Yes, that notification exists, in a way. The label will be drawn during the next iteration of the run loop. So do your login at the end of the next run loop iteration, for instance using a performSelector:afterDelay:0 or maybe using
dispatch_async (dispatch_get_main_queue (), ^{ [self login]; });
But a) this depends on the order of execution of rendering versus timers and dispatch_queues. If rendering happens before timer execution, you're all set.
And b) don't block the main thread. Try to perform the login in a background thread/concurrent queue, or do it asynchronously on the main thread if you're using, e.g., NSURLConnection.