简体   繁体   中英

NSString in custom class -> gets added to dictionary -> when gets retrieved NSString value has changed?

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.

  1. In loadFiles you overrelease fileName and fileName2 .
  2. the fileName property should be declared copy instead of retain .

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 and fileName2 variables are being over-released, likely leading to a crash. And the fileName property should be copy , not retain . 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM