Unable to put NSSound instance in NSMutableArray ion cocoa - objective-c

I'm having trouble adding NSsound instances into an NSMutableArray
The error I get is that I cannot do [soundFiles addObject:soundObj], because soundObj is nil. But if I do [soundObj play], it will play - so it is an instance.
//Name of all the sounds to load in.
sounds = [NSArray arrayWithObjects:
#"sound1",
#"sound2",
nil];
for (int i = 0; i < sounds.count; i++) {
NSString *soundName = [NSString stringWithString:sounds[i]];
NSSound *soundObj = [NSSound soundNamed:soundName];
[soundFiles addObject:soundObj];
}
If I change [soundFiles addObject:soundObj] to [soundFiles addObject:soundName] it is fine, so it is something with trying to pass the NSSound
I'm hoping to be able to preload a bunch of very short sounds to make them play the minute they are called. milliseconds matter for this project.
I was hoping this would allow me to do [[soundFiles objectAtIndex:1] play] - as I was thinking that would result it being faster than creating the NSSound object when it is time to play it.

You've probably figured this out by now, but my guess is that you were writing the code before you had created ALL the sound files you wanted to work with. So when you tried to create an NSSound instance of those nonexistent files, it got set to nil. It's hard to troubleshoot because NSSound doesn't throw an error when you point it to a non-existent file.
I had the identical problem until I created all the sound files I wanted to use. I just wanted to post my answer to help anyone else who's having this problem because a Google search turns up nothing that helps.

Related

Why does calling removeAllObjects twice on NSArray cause app to crash?

Using the following code snippet on NSArray one time works but if I call it a second time directly after the app crashes, I don't really understand why, I would have thought that it would blindly remove all objects if any in the array? If thats not true can I test with count > 0 before I run it to be sure the app won't crash or is there a better way?
[_transactionRowsRows removeAllObjects];
NSArray is imutable, you need to use NSMutableArray.

Confusion with Passing Obj-C objects into methods and retaining the changes to that object

I'm writing a program and trying to learn more about threads, multiprocessing, and such.
My architecture is a Model/View/Controller type.
I have my own subclass of NSImageView (ThumbnailView) and I wanted to be clever and have it listen for a message to clear itself (so all the thumbnails just clear themselves without me having to loop through them).
The problem is my ThumbnailView is controlled by a ThumbnailViewController which is really the one listening for the message. When it gets the message it spins off a new thread with a class object that is the command (ClearThumbnailViewCommand). It passes as an argument a dictionary item containing the associated ThumbnailView object and a key. Within the ClearThumbnailViewCommand I set the image of the ThumbnailView object to be some neutral image (like gray.jpg).
All this works fine, however, the Thumbnail object that changed is not the same Thumbnail object that went in. So I figure I need to pass a pointer rather than the object. I remember something about using MyObject** as opposed to MyObject* and passing via &MyObject but I can't seem to untangle the various combinations. Not being able to reason it out I fell back to my, normally, foolproof system of trying random combinations of things but this time it's not helping.
It seems that even if I'm able to construct a class that passes a pointer (Not sure if I'm using these terms correctly), I'm not able to assign it correctly to the NSDictionary, which doesn't want an id** .
I'll try and include the basics below, if that helps at all.
ThumbnailVew : NSImageView {
ThumbnailVewController * _controller;
}
init {
_controller = [[ControllerClass alloc] initWithControlObject: &self];
}
ThumbnailVewController : ControllerClass {
id ** _controlObject;
}
initWithControlObject: (id**)object {
_controlObject = object;
}
Then when messages are posted a ThumbnailVewController method is called which ultimately does this…
Which, of course will not let me pass in &_controlObject
when it is all re-written so that I can pass _controlObject, I don't get an error, however the ThumbnailView I change is only local to the method.
if([command isEqualToString:#"CLEAR_THUMBNAILS"]) {
NSDictionary * dict;
dict = [[NSDictionary alloc] initWithObjectsAndKeys: &_controlObject, #"thumbnail", nil];
[self newThreadWithCommand:[[[ClearThumbnailViewCommand alloc] initWithArgument:dict] autorelease]];
}
Is this even possible?
Thanks for the feedback. I am indeed just trying to explore some various situations. I understand I may be going a round about way to get at some things but it does help me understand the boundaries more clearly.
In case it might help anyone else, I found a solution to my particular issue. As it turns out I was making the reference to the _controlObject in the init phase of the ThumbnailView. The object created during that phase is different than the ThumbnailView object created when awakeFromNib is called.
when I move the _controlObject assignment to the awakeFromNib method all works as I expected. (Of course I reset the code to not include any of the fancy ** and & declarations.
Again, thank you for helping me understand a bit more about this language. I'm starting to like it quite a bit.

MPMoviePlayer does not repeat when set to MPMovieRepeatModeOne

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.

Problems with NSTextView where NSTextField worked

I'm just starting out with obj-c, I'm trying to build an importer to fetch a couple numbers from a piece of formatted text. I started with a wrapping TextField and was able to both get the text into a string and search it as I wanted with
NSString *varImport = [NSString stringWithString:[importTextView stringValue]];
When I switch over to the TextView in the Interface Builder I get there error
-[NSScrollView string]: unrecognized selector sent to instance 0x100429160
I think this may be the root of my problem, although I am dragging over a TextView when I look in the inspector panel its labeled as a ScrollView which I'm not familure with yet.
Through my research I came across two different sites saying that TextView wont directly feed into a string but it was for odd reasons, IE TextView stored the data as a MutableString that was constantly updating and to access it you had to copy the original (Second example) Anyways I'm turning to the experts because I'm clearly doing something wrong and I can't make sense of the answers on the web.
NSString *varImport = [NSString stringWithString:[[import textStorage] string]];
NSString *varImport = [[import string] copy];
Don't worry so much about the other stuff if you don't have the time to explain what's going in the web examples, I mainly want to know about the ScrollView stuff and how to get a string out of it to be able to search it.
Thanks in advance!
Graham
Simply because you are using the wrong method.
NSString *varImport = [NSString stringWithFormat:[importTextView string];
Also, you might have connected the importTextView instance variable to the wrong connection. A NSTextView is always in a NSScrollView. Simply just right-click your object from interface builder and drag the instances to the top of the textview (Where it should show "NSTextView").
Save the nib and try run it again. It should work.
To get text value from a text field or text view you should use - (NSString *)string method.

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...