I am trying to stream a file on the iphone using OpenAL. Most of this code is based on the excellent tutorial by Ben Britten; however, I have tried to store the data in a custom class within a dictionary rather than a dictionary within a dictionary.
I have set up the custom class to hold the information about the file I will be streaming. In here contains an NSString called fileName which holds the location of my file. It is this variable that changes its value upon retrieval. Custom class is defined as so:
@interface DBuffer : NSObject {
NSString *fileName ;
UInt32 fileSize ;
UInt32 bufferSize ;
UInt32 bufferIndex ;
ALenum bufferFormat ;
ALsizei bufferFreq ;
BOOL bufferLoops ;
}
@property (nonatomic, retain) NSString *fileName ; *<-----;
@property (nonatomic, assign) UInt32 fileSize ;
@property (nonatomic, assign) UInt32 bufferSize ;
@property (nonatomic, assign) UInt32 bufferIndex ;
@property (nonatomic, assign) ALenum bufferFormat ;
@property (nonatomic, assign) ALsizei bufferFreq ;
@property (nonatomic, assign) BOOL bufferLoops ;
-(void)dealloc ;
@end
@implementation DBuffer
@synthesize fileName ;
@synthesize fileSize ;
@synthesize bufferSize ;
@synthesize bufferIndex ;
@synthesize bufferFormat ;
@synthesize bufferFreq ;
@synthesize bufferLoops ;
-(void)dealloc
{
[fileName release];
[super dealloc];
}
@end
Each sound is stored in a dictionary like so:
-(void)loadFiles
{
NSMutableDictionary* tempLibrary = [[NSMutableDictionary alloc] init];
NSString* fileName = [[NSBundle mainBundle] pathForResource:@"b2" ofType:@"caf"];
[tempLibrary setObject:[self initializeStreamFromFile: fileName format:AL_FORMAT_STEREO16 freq:44100 ] forKey: @"101" ];
[fileName release];
NSString* fileName2 = [[NSBundle mainBundle] pathForResource:@"a5" ofType:@"caf"];
[tempLibrary setObject:[self initializeStreamFromFile: fileName2 format:AL_FORMAT_STEREO16 freq:44100 ] forKey: @"102" ];
[fileName2 release];
self.soundLibrary = tempLibrary;
[tempLibrary release];
NSLog(@"load files: tempLibrary开发者_运维技巧 %@<%x>", tempLibrary, &*tempLibrary);
NSLog(@"load files: soundLibrary %@<%x>", soundLibrary, &*soundLibrary);
NSLog(@"load files: soundLibrary retaincount %d", [soundLibrary retainCount]);
NSLog(@"load files: tempLibrary retaincount %d", [tempLibrary retainCount]);
DBuffer *retrieve = [soundLibrary objectForKey:@"101"];
NSLog(@"retrieve: %@",retrieve.fileName);
NSLog(@"retrieve retaincount: %d",[retrieve.fileName retainCount]);
}
It calls on the initializeStreamFromFile, which is defined below. After it returns and adds the classes to the dictionary, I do a check to try and retrieve the filename from the dictionary with the loadFiles instance method and it works fine, returning:
"retrieve: /Users/david/Library/Application Support/iPhone Simulator/4.2/Applications/7DA24283-8D58-49B8-BBEB-48B08D921367/OpenALRefine.app/b2.caf"
Which is the correct address, same as spat to the log at the point of initialisation shown below, and
"retrieve retaincount: 2"
// this queues up the specified file for streaming
-(DBuffer*)initializeStreamFromFile:(NSString*)fileName format:(ALenum)format freq:(ALsizei)freq
{
// first, open the file
AudioFileID fileID = [self openAudioFile:fileName];
// find out how big the actual audio data is
UInt32 fileSize = [self audioFileSize:fileID];
UInt32 bufferSize = kOPENAL_STREAMING_BUFFER_SIZE;
UInt32 bufferIndex = 0;
DBuffer* DBufferID = [[[DBuffer alloc] init] autorelease];
DBufferID.fileName = fileName ;
DBufferID.fileSize = fileSize ;
DBufferID.bufferSize = bufferSize ;
DBufferID.bufferIndex = bufferIndex ;
DBufferID.bufferFormat = format ;
DBufferID.bufferFreq = freq ;
NSLog(@"DBufferID initialised %@<%x>", DBufferID, &*DBufferID);
//output of this is: "DBufferID initialised <DBuffer: 0x533a5f0><533a5f0>"
NSLog(@"DBufferID.fileName initialised %@<%x>", DBufferID.fileName, &*DBufferID.fileName);
//output of this is: "DBufferID.fileName initialised /Users/david/Library/Application Support/iPhone Simulator/4.2/Applications/7DA24283-8D58-49B8-BBEB-48B08D921367/OpenALRefine.app/b2.caf<55453a0>"
NSLog(@"DBufferID.fileName retaincount %d", [DBufferID.fileName retainCount]);
//output of this is: "DBufferID.fileName retaincount 3"
AudioFileClose(fileID);
return DBufferID;
}
Ok, so far so good. In the nib I have a simple interface with a play button, attached to a playStream method. I attempt to run the same code at the start of the method, like so:
- (NSUInteger)playStream:(NSString*)soundKey gain:(ALfloat)gain pitch:(ALfloat)pitch loops:(BOOL)loops
{
NSLog(@"PlayStream activiated");
ALenum err = alGetError(); // clear error code
NSLog(@"soundLibrary retaincount %d", [soundLibrary retainCount]);
DBuffer *retrieve = [soundLibrary objectForKey:@"101"];
NSLog(@"retrieve initialised %@<%x>", retrieve, &*retrieve);
NSLog(@"retrieve.fileName initialised %@<%x>", retrieve.fileName, &*retrieve.fileName);
However, the results from the last to NSLogs are:
"retrieve initialised <533a5f0>"
So far so good, the same memory address as was sent to the dictionary. However, when I print the value of retrieve.fileName is returns a random string, like so:
"retrieve.fileName initialised hu.lproj<55453a0>"
You'll note the memory address hasnt changed from the one shown in the initializeStreamFromFile method printed above. The value of this variable is seemingly random, other values it has returned as are:
" "/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/System/Library/PrivateFrameworks/WebKit.framework"
etc.. or simply "EXC_BAD_ACCESS"
I am relatively new at objective C, but im guessing its obviously something to do with poor memory management. I've been reading up on apple pdfs on memory management to try and fix but to no avail. Any insights would be appreciated.
- In
loadFiles
you overreleasefileName
andfileName2
. - the
fileName
property should be declaredcopy
instead ofretain
.
There are a number of issues with this code that indicate that a re-read of the Objective-C concepts guide would be helpful (in particular, it looks like you might have a bit of C++ experience and are making assumptions based on that knowledge?). I re-read that particular document once every few months for the first few years that I wrote Objective-C code; very helpful.
In particular:
&*retrieve.fileName
effectively does nothing. Drop the&*
.retainCount
is useless. The retain count of an object is an internal implementation detail. Treat retain counts purely as deltas; if you cause it to be increased, you must cause it to be decreased when you are done with the object.(as Nikolai said) your
fileName
andfileName2
variables are being over-released, likely leading to a crash. And thefileName
property should becopy
, notretain
. The memory management guide does a good job discussing retain/release details.
I'd bet that "build and analyze" would have caught most/all of the retain release problems.
精彩评论