MPMoviePlayer does not repeat when set to MPMovieRepeatModeOne - objective-c

This one is baffling me. If anyone has any answers, they are appreciated.
I have the following method which plays a video during a loading process in my app:
-(void)playLoadingMovie
{
NSString *moviePath = [[NSBundle mainBundle] pathForResource:#"movie" ofType:#"mp4"];
movieController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
movieController.moviePlayer.repeatMode = MPMovieRepeatModeOne;
[movieController.moviePlayer setControlStyle:MPMovieControlStyleNone];
[movieController.view setFrame:CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width)];
[self.view addSubview:movieController.view];
NSLog(#"repeatMode: %d",movieController.moviePlayer.repeatMode);
}
Everything is properly declared, synthesized, released, etc... in the appropriate places and situations elsewhere in the code. This particular method works just fine, except for the fact that it does not repeat as it should.
You can see that the repeatMode is set to MPMovieRepeatModeOne, and when I run the code, the log statement prints out "repeatMode: 1" just as it should.
I know that I can do something hackish and set an observer for when the movie finishes and have it call a method to play the movie again, but I would much rather have this code work properly.
Any ideas?

I have discovered the reason why it is not repeating. After I start the movie, I start a number of memory intensive processes, and unfortunately for me, I have so much happening on the main thread that when the movie finishes there isn't enough memory available for it to repeat. Lame.
So there is nothing wrong with the code.
Now I will be exploring using other threads for some of the processes in order to make room on the main thread for the movie to repeat.

This may just be the same thing you are doing but sometimes accessing properties dosen't work. You should try setRepeatMode.

Related

Reset Core Data driven treeController content

I run my program that creates Core Data content that is displayed in a NSOutlineView using NSTreeController. The second time I run my program I want to clean the content of my NSTreeController and I run the method pasted below. The method either hangs for a long time (600 seconds) before it finishes or it crashes. If I have few entities (500-1000) in my NStreeController it takes much less time compared to if I have a lot (200,000) entities to pass this method, if it passes at all. What I need to know is if there is a better way to clear/refresh/reset the content of my NStreeController to clear my NSoutlineView before I re-run my program and fill up the NStreeController again. Specifically, I would like my NSOutlineView to respond quickly to changes to the contents of my NSTreeController, and I need the content of my Core Data driven NSTreeController to be able to be reset.
-(void) cleanSDRDFileObjects
{
__weak __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.outlineView collapseItem:nil collapseChildren:YES];
[weakSelf.coreDataController._coreDataHelper.context performBlockAndWait:^{
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:#"SDRDFileObject" inManagedObjectContext:weakSelf.coreDataController._coreDataHelper.context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSArray * result = [weakSelf.coreDataController._coreDataHelper.context executeFetchRequest:request error:nil];
for (id fileobject in result){
[weakSelf.coreDataController._coreDataHelper.context deleteObject:fileobject];
}
[weakSelf.coreDataController._coreDataHelper.context processPendingChanges];
NSLog(#"Finished deleting all objects");
}];
});
}
The managedobjectcontext (context) is run as type NSMainQueueConcurrencyType and the method is run on the main thread. Suggestions for improvements, or useful examples for the combination of reset/refreshing NSOutlineView + Core Data would be greatly appreciated. Thanks. Cheers, Trond
In response to #TomHarringtons question I took a picture of my Time Profiler. I really dont understand why it hangs on this method, however, after commenting this methods out (```processPendingChanges```), it still hangs and takes forever to finish (6 minutes). It seems the process gets stuck on the main thread and cant continue.
When I rerun the application, with processPendingChanges commented out its still hanging.
Update
I believe I solved this but I am slightly uncertain as to why this worked. It seems that my first method went into an indefinite loop that did not release its objects. The following simple solution worked:
__weak __typeof__(self) weakSelf = self;
dispatch_sync(dispatch_get_main_queue(), ^{
[weakSelf.coreDataController._coreDataHelper.context reset];
});
I was certain that to properly empty a managed object context I would have to delete each entity individually. The reset function seems pretty brute force and does it actually clean up memory and make sure everything is okay? If anyone wants to shed some light on this that would be appreciated.
Looking at this again, you fetched all objects of a type in performBlockAndWait -- this blocks the main thread because you have mainQueueConcurrency and you used the andWait version of performBlock.
You then delete each object one-by-one. These objects are in a tree data structure with a outlineview attached (see the KVO messages in the stack trace). These objects have to-many relationships that need to be maintained by core data, hell, you could even have a cascading delete rule. (see propagateDelete and maintainInverseRelationship in the stack trace) In any event, you start requesting that both the data source and the view start doing a lot of work, on the main thread. You could try using a child MOC with privateQueueConcurrency if you wanted to iterate all objects in the background.
But, like the comments indicated:
NSManagedObjectContext's reset most definitely frees up memory, and it's fine for what you want to do here: blow everything away.
It begs the question why you load the model from the store on disk in the first place, though.
If you want Core Data, but not persistence between the times you run the program, you can initialize the persistentStoreCoordinator with a store of NSInMemoryStoreType rather than pointing it to a file URL.

Status application: sometimes panels are missing, multithreading possible issue

I have a status application, it's long to post it all so I'll describe it and post only part of the code:
In the xib file there are two objects: AboutController and PreferencesController;
The app delegate is able to launch AboutController's and PreferencesController's panels;
The panels are in the xib file too;
The user by selecting a status menu item is able to launch these two panels;
There is a timer that constantly downloads a HTML page and reads it;
When the page is downloaded, the stringValue of a label is changed.But the stringValue may also be changed from the PreferencesController.The page is downloaded from a background thread, but it's changed through the main queue.
Now I've got some questions:
Do I have to invalidate the timer when the application starts sleeping (computer goes in standby),and create another one when it returns on?
The label is updated in the main queue, so I still have to protect the label access with a mutex?
Sometimes the panels are missing: at the start of the application I am able to launch panels by clicking a menu item, but sometimes they are not launching.I don't know how to reproduce this bug always, it just happens randomly when the application is active for 2/3 hours usually, I have to relaunch the application to go around this.
The code is too long, that's only a piece of code:
- (void) checkPosts: (id ) sender
{
NSOperationQueue* queue=[NSOperationQueue new];
queue.maxConcurrentOperationCount=1;
[queue addOperationWithBlock:^
{
NSNumber* newPosts= [self updatePosts];
NSNumber* posts= [controller posts];
if([posts integerValue]!=[newPosts integerValue])
{
NSOperationQueue* queue=[NSOperationQueue mainQueue];
posts= newPosts;
[queue addOperationWithBlock:^
{
// This is where I may have a race condition
item.attributedTitle=[[NSAttributedString alloc]initWithString: [formatter stringFromNumber: posts] attributes: #{NSForegroundColorAttributeName : [controller color], NSFontAttributeName : [NSFont userFontOfSize: 12.5]}];
}];
// That's not so relevant:
NSUserNotification* notification=[NSUserNotification new];
notification.title= [NSString stringWithFormat: #"posts Changed to %#",posts];
notification.deliveryDate=[NSDate date];
notification.soundName= NSUserNotificationDefaultSoundName;
NSUserNotificationCenter* center=[NSUserNotificationCenter defaultUserNotificationCenter];
[center deliverNotification: notification];
center.delegate= self;
[controller setPosts: posts];
}
}];
}
A little background information:
This method works in a background thread;
[self updatePosts] downloads the HTML page and returns the number of posts;
[controller posts] reads the previous number of posts using NSUserDefaults;
item is a status menu's menu item.
More Details
This is how I get the timer:
// In the applicationDidFinishLaunching method
timer=[NSTimer scheduledTimerWithTimeInterval: [interval integerValue ] target: self selector: #selector(checkReputation:) userInfo: nil repeats: YES];
timer is a property:
#property (nonatomic, strong) NSTimer* timer;
interval is a NSNumber, for sure it's integer value is greater or equal than 1.
It's not entirely clear what's happening here. You've provided plenty of information but not everything needed to give a definitive answer. I'll try to address your questions first:
Do I have to invalidate the timer when the application starts sleeping
(computer goes in standby),and create another one when it returns on?
Have to? No. Should for cleanliness and certainty of the state? Yes, probably. You should probably specify exactly how you set up the timer since you can run into problems with it interacting with the run loop ... but I don't think this is your problem.
The label is updated in the main queue, so I still have to protect the
label access with a mutex?
As long as you update the UI from the main thread/queue, you should be fine. This is a standard design approach with blocks.
Sometimes the panels are missing: at the start of the application I am
able to launch panels by clicking a menu item, but sometimes they are
not launching.I don't know how to reproduce this bug always, it just
happens randomly when the application is active for 2/3 hours usually,
I have to relaunch the application to go around this.
If you don't know how to reproduce it, I'm not sure we can help you beyond "places to look." The first thought I've got is that you may be recreating multiple copies of your primary controllers when the app becomes active (since you asked about this earlier, I assume you've tried doing something with it). Make sure the same controllers are being reused.
Now on to the code.
NSOperationQueue* queue=[NSOperationQueue new];
The queue variable is local to the scope of the method. I see no retain/release, so I assume you're using ARC. In that case, you're not retaining the new queue you're creating and its life span is not guaranteed to survive as long as you need it to once the method completes and you've left its scope. You should make queue an instance variable so it sticks around. This way the queue can be reused every time the method is fired and it'll stay around long enough for other queues/threads to use.
I think it's likely this is your biggest culprit. Adjust it and update your question to reflect how it affects the condition of your app.

Instruments and memory leaks

I'm using the leaks tool in XCode's instruments to find memory leaks (go figure). I have a few leaks each time I run my app at different points in the app. I look at the extended details, and I'm never pointed to any of the code I wrote, only code built into the foundation of xcode. Two examples of this are:
http://imageshack.us/photo/my-images/192/screenshot20110728at102.png/
http://imageshack.us/photo/my-images/853/screenshot20110728at102.png/
As you can see, some of the problems come from the Message UI library. The only place I use that is here:
-(void)displayComposerSheet
{
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;
[mail setSubject:#"Suggestions"];
[mail setToRecipients:[NSArray arrayWithObjects:#"sampleEmail#gmail.com", nil]];
[self presentModalViewController:mail animated:YES];
[mail release];
}
-(void)launchMailAppOnDevice
{
NSString *recepient = [NSString stringWithFormat:#"mailto:sampleEmail#gmail.com"];
recepient = [recepient stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:recepient]];
}
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
[self dismissModalViewControllerAnimated:YES];
}
How can I fix this? Thanks!
Finding out where a leak comes from is not always an easy task, helping remotely is even more complex.
In your case, what could help is the little arrow that is shown next to the memory address of the leak. If you click on it, you should get shown an information pane with the full stack trace at that moment. Inspect the list of methods presented there and look for some method of yours, then click on it to inspect the code.
There is not much else you can do in Instruments that get an idea about where the leaked object was created. You should then figure out where you could have missed releasing it.
As to Apple SDK, there are a few memory leaks that are reported and that you can find out there on the web, but they are really seldom.
If Instruments does not help you, one important consideration for you is: what was your app doing when Instruments reported the leak? Retrieving data from the web, displaying a new view, whatever... this could help in delimiting the field of your further investigation. If the leaks shows up multiple times, this could also be a great value to identify what part of your program could be producing it.
If you get a clue about that, then go check the code that accomplishes that. One technique (I know that it might sound strange, but try it) is removing/commenting out chunks of code and then checking to see whether the leak is still there.
"Removing/commenting out chunks of code" may mean many things, from not making a web request, to avoid using a class of a type and replacing it with another. This is not always a simple process, because you impair the app functionality, so you have to use your knowledge about how remove functionality and leave your app "testable". If you are lucky, this could help you further delimiting the code that is causing the leak.
Keep in mind that also the static analysis tool could help, and that leaks is not perfect, sometimes it is wrong. There is another technique to discover leaks that is not based on Leaks, but on Object Allocation. This is very powerful in my experience and I strongly encourage you to try that also, although I suspect that it will not help in this case. It is called heapshot analysis.

Significant lag when loading image using UIImage from URL asynchronously

I am trying to write an iPad app that loads an image from a URL. I am using the following image loading code:
url = [NSURL URLWithString:theURLString];
NSData *data = [NSData dataWithContentsOfURL:url];
img = [[UIImage alloc] initWithData:data];
[imageView setImage:img];
[img release];
NSLog(#"Image reloaded");
All of that code gets added to a NSOperationQueue as an operation so it will load asynchronously and not cause my app to lock up if the image's websever is slow. I added the NSLog line so I could see in the console when this code finished executing.
I have noticed consistently that the image is updated in my app about 5 seconds AFTER the code finishes executing. However if I use this code on it's own without putting it in the NSOperationQUeue it seems to update the image almost immediately.
The lag is not caused entirely by a slow web server... I can load the image URL in Safari and it takes less than a second to load, or I can load it with the same code without the NSOperationQueue and it loads much more quickly.
Is there any way to reduce the lag before my image is displayed but keep using a NSOperationQueue?
According to the documentation, the code you have written is invalid. UIKit objects may not be called anywhere but on the main thread. I'll bet that what you're doing happens to work in most respects but doesn't successfully alter the display, with the screen being updated by coincidence for some other reason.
Apple strongly recommend that threads are not the way to perform asynchronous URL fetches if you want to remain battery efficient. Instead you should be using NSURLConnection and allowing the runloop to organise asynchronous behaviour. It's not that hard to write a quick method that just accumulates data to an NSData as it comes then posts the whole thing on to a delegate when the connection is complete but assuming you'd rather stick with what you've got I'd recommend:
url = [NSURL URLWithString:theURLString];
NSData *data = [NSData dataWithContentsOfURL:url];
[self performSelectorOnMainThread:#selector(setImageViewImage:) withObject:data waitUntilDone:YES];
...
- (void)setImageViewImage:(NSData *)data
{
img = [[UIImage alloc] initWithData:data];
[imageView setImage:img];
[img release];
NSLog(#"Image reloaded");
}
performSelectorOnMainThread does what the name says — the object is sent to will schedule the selector requested with the object given as a single parameter on the main thread as soon as the run loop can get to it. In this case 'data' is an autoreleased object on the pool in the thread implicitly created by the NSOperation. Because you need it to remain valid until it has been used, I've used waitUntilDone:YES. An alternative would be to make data something that you explicitly own and have the main thread method release it.
The main disadvantage of this method is that if the image returns in a compressed form (such as a JPEG or a PNG), it'll be decompressed on the main thread. To avoid that without making empirical guesses about the behaviour of UIImage that go above and beyond what is documented to be safe, you'd need to drop to the C level and use CoreGraphics. But I'm taking it as given that doing so is beyond the scope of this question.
Tommy is correct about needing to do all UIKit stuff on the main thread. However, if you're running the fetch on a background operation queue, there's no need to use the NSURLConnection asynchronous loading. Also, by keeping the image decoding work on the background operation, you'll keep the main thread from blocking while decoding the image.
You should be able to use your original code as is, but just change [imgView setImage:img] to:
[imageView performSelectorOnMainThread:#selector(setImage:)
withObject:img
waitUntilDone:NO];

Can't figure out where memory leak comes from

I'm somewhat of a cocoa newbie and I simply can't figure out why I'm getting a spike in the leaks graph in Instruments with this code. It seems to be a small leak (i.e. 16 Bytes and the Leaked Object is "Generalblock-16"; that is the only leaking object and says Self 100%) and it seems to remain that size regardless of whether I choose just 1 file or 12,000 files. I've double-clicked on every line of the Stack Trace shown in the Extended Detail view of Instruments and it doesn't lead me to any line in my code.
Thanks in advance for any help you can give me with this.
Y.V.
P.S.: In case there's nothing wrong with my code (which I doubt) and the leak is simply a bug or something unrelated to my code, is it safe to use this code as it is? Will it bring about instability to my app or make crash or anything like that?
#implementation AppController
-(IBAction)openTheOpenPanel:(id)sender
{
NSOpenPanel *openThePanel = [NSOpenPanel openPanel];
[openThePanel setAllowsMultipleSelection:YES];
if([openThePanel runModal] == NSOKButton)
{
NSArray *allTheFiles = [openThePanel filenames];
int theNumberOfFiles = [allTheFiles count];
int i;
NSMutableDictionary * theDict;
theDict = [[NSMutableDictionary alloc] init];
for (i=0; i < theNumberOfFiles; i++) {
NSString *thisFile = [allTheFiles objectAtIndex:i];
NSString *theFileNum = [NSString stringWithFormat:#"%i", i];
[theDict setObject:thisFile forKey:theFileNum];
}
[theDict writeToFile:#"/tmp/test_file.txt" atomically:YES];
[theDict release];
}
}
#end
Try running CLang Static Analyzer on your code
http://clang-analyzer.llvm.org/
And fix every single thing it points out. I have never seen it point out something that was wrong, even though sometimes I was sure that it was. It's specifically great at finding leaks and other reference related issues.
Not that it will solve anything, but I recommend always changing
NSMutableDictionary * theDict;
to
NSMutableDictionary * theDict = nil;
Otherwise theDict wil probably have some weird - and unknown - memory address until you alloc/init it. If you do this:
NSMutableDictionary * theDict;
if (theDict) {
// condition is true
}
the condition will be met, even though you haven't initialized theDict yourself.
Your code looks good! There aren't any memory leaks in what you've shown. If you want, you can declare theDict like this:
theDict = [[[NSMutableDictionary alloc] init] autorelease];
Calling "autorelease" will add the object to the autorelease pool, and it will be automatically released once your function has executed. It's handy - because you don't have to remember to call release. Either way will work here - though.
Does your app leak each time you call this function, or just the first time? If Instruments doesn't show you a line of code where the leak originates, odds are it's just something in the system. In my experience, a few small leaks happen every so often from random system issues.
EDIT:
A leak of this size shouldn't cause any instability in your app. When you're looking for memory leaks, you want to watch out for:
Leaks involving large chunks of
memory (NSData or NSImage objects, etc...)
Leaks inside loops, or in functions
called repeatedly (that will add up
to be significant).
Even on the iPhone (where your app gets about 28MB of RAM, max), a few leak of 16 bytes or 32 bytes isn't a big deal. Generally, Instruments reports a few leaks right when the app launches - and those aren't a big problem. You just want to make sure you aren't leaking more and more memory as your app runs - because a serious user could keep running your app until all available memory was leaked. The OS won't reuse leaked memory because it thinks your app is still using it - so eventually you'll be unable to allocate memory for new objects.
Hope that helps!
1) Check to make sure that you don't have NSZombieEnabled set to YES in the executable environment variables .
2) instead of calling :
theDict = [[[NSMutableDictionary alloc] init] autorelease];
You should be able to simply call:
theDict = [NSMutableDictionary dictionary];
They are essentially the same thing.
Hey guys! Thanks so much for your prompt answers!
Ben, thank you very much for your suggestion. Autoreleasing the dictionary like you suggested was actually my first approach to the code but it leaked, so I changed my code from autoreleasing to allocating and releasing manually and, unfortunately, it leaks just as much (same object and amount of leak).
In case it is not my code what's causing the leak and -as you mentioned- is just something in the system, do you think it is safe to use my code despite the existence of the slight leak? or will it cause instability or crashes in my app?
I've done extensive testing the way it is and so far it has not shown any problems in any of my tests (I've only noticed the presence of the leak by using instruments).
Thanks again for your help!
valgrind, the king of leak detectors, has been ported to OS X. valgrind will find your memory leak and tell you exactly where the allocation site was, and even more important, what was on the call stack when the object was allocated. valgrind is a lifesafer!
In that code, you have four possible lines that could be responsible for a leak:
NSOpenPanel *openThePanel = [NSOpenPanel openPanel];
[openThePanel setAllowsMultipleSelection:YES];
if([openThePanel runModal] == NSOKButton)
NSArray *allTheFiles = [openThePanel filenames];
Try commenting out each one - mocking up data when you comment out the openPanel call or ask it for filenames, and pretending runModal was called for the if statement.
Comment them out one at a time and test for leaks, then you can see which line is responsible and follow it back...