自定义类中的 NSString ->被添加到字典 ->什么时候获取的NSString值已经改变了?

发布于 2024-10-20 03:43:28 字数 5589 浏览 9 评论 0原文

我正在尝试使用 OpenAL 在 iPhone 上传输文件。此代码大部分基于 Ben Britten 的优秀教程;但是,我尝试将数据存储在字典中的自定义类中,而不是字典中的字典中。

我已经设置了自定义类来保存有关我将要流式传输的文件的信息。这里包含一个名为 fileName 的 NSString,它保存我的文件的位置。正是这个变量在检索时改变了它的值。自定义类定义如下:

@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

每个声音都存储在字典中,如下所示:

-(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]);

}

它调用initializeStreamFromFile,其定义如下。在它返回并将类添加到字典中之后,我进行检查以尝试使用 loadFiles 实例方法从字典中检索文件名,并且它工作正常,返回:

“检索:/Users/david/Library/Application Support/iPhone 模拟器/4.2/Applications/7DA24283-8D58-49B8-BBEB-48B08D921367/OpenALRefine.app/b2.caf"

这是正确的地址,与下面所示的初始化点处的日志相同,并且

“检索保留计数: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;
}

好的,到目前为止一切顺利。在笔尖中,我有一个带有播放按钮的简单界面,附加到 playStream 方法。我尝试在方法开始时运行相同的代码,如下所示:

- (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);

但是,最后一个 NSLogs 的结果是:

“检索初始化的<533a5f0>”

到目前为止一切顺利,内存地址与发送到字典的内存地址相同。但是,当我打印retrieve.fileName 的值时,返回一个随机字符串,如下所示:

“retrieve.fileName 已初始化 hu.lproj<55453a0>”

您会注意到内存地址与上面打印的initializeStreamFromFile 方法中显示的地址相比没有改变。该变量的值看似随机,它返回的其他值如下:

” “/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator4.2.sdk/System/Library/PrivateFrameworks/WebKit.framework”

等..或简单的“EXC_BAD_ACCESS”

我对目标C相对较新,但我猜测它显然与内存管理不善有关。我一直在阅读有关内存管理的苹果pdf文件,尝试修复但无济于事。任何见解将不胜感激。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

吃颗糖壮壮胆 2024-10-27 03:43:28
  1. loadFiles 中,您过度释放了 fileNamefileName2
  2. fileName 属性应声明为 copy 而不是 retain
  1. In loadFiles you overrelease fileName and fileName2.
  2. the fileName property should be declared copy instead of retain.
舟遥客 2024-10-27 03:43:28

此代码存在许多问题,表明需要重新阅读Objective-C 概念指南 会很有帮助(特别是,看起来您可能有一点 C++ 经验,并且正在根据这些知识做出假设?)。在我编写 Objective-C 代码的头几年里,我每隔几个月就会重读一次那个特定的文档;非常有帮助。

特别是:

  • &*retrieve.fileName 实际上什么也不做。删除 &*.

  • retainCount 没有用。对象的保留计数是内部实现细节。将保留计数纯粹视为增量;如果你让它增加,当你使用完该对象时,你必须让它减少。

  • (正如 Nikolai 所说)您的 fileNamefileName2 变量被过度释放,可能会导致崩溃。并且fileName属性应该是copy,而不是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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文