在 Objective-C 中使用数组试图简化大量重复代码
Posted
技术标签:
【中文标题】在 Objective-C 中使用数组试图简化大量重复代码【英文标题】:Using arrays in Objective-C trying to simplifying a lot of repeating code 【发布时间】:2012-09-08 20:28:22 【问题描述】:您好 *** 大师。这是我在这里的第一个问题,所以我很高兴能直接加入。
我正在尝试更好地理解 ios 数组,但遇到了难题。我正在制作一个使用 FMOD 的声音应用程序。我的一切工作正常,但我有 9 个按钮,它们的功能几乎完全相同,只是每个按钮在按下时播放不同的 .wav 文件,然后在释放时停止声音。我想将它放入一个数组中并简化和缩短我的代码,这就是我迷路的地方。我剥离了代码以显示我目前正在做的事情。有什么想法吗?
.h
@interface
FMOD::Sound *sound1;
FMOD::Sound *sound2;
FMOD::Sound *sound3;
FMOD::Sound *sound4;
FMOD::Sound *sound5;
FMOD::Sound *sound6;
FMOD::Sound *sound7;
FMOD::Sound *sound8;
FMOD::Sound *sound9;
- (IBAction)playSound1:(id)sender;
- (IBAction)stopSound1:(id)sender;
- (IBAction)playSound2:(id)sender;
- (IBAction)stopSound2:(id)sender;
- (IBAction)playSound3:(id)sender;
- (IBAction)stopSound3:(id)sender;
- (IBAction)playSound4:(id)sender;
- (IBAction)stopSound4:(id)sender;
- (IBAction)playSound5:(id)sender;
- (IBAction)stopSound5:(id)sender;
- (IBAction)playSound6:(id)sender;
- (IBAction)stopSound6:(id)sender;
- (IBAction)playSound7:(id)sender;
- (IBAction)stopSound7:(id)sender;
- (IBAction)playSound8:(id)sender;
- (IBAction)stopSound8:(id)sender;
- (IBAction)playSound9:(id)sender;
- (IBAction)stopSound9:(id)sender;
米。
- (void)viewWillAppear:(BOOL)animated
[[NSString stringWithFormat:@"%@/sound1.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound1);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound2.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound2);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound3.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound3);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound4.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE, NULL, &sound4);
ERRCHECK(result);
result = sound4->setMode(FMOD_LOOP_NORMAL);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound5.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound5);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound6.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound6);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound7.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound7);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound8.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound8);
ERRCHECK(result);
[[NSString stringWithFormat:@"%@/sound9.wav", [[NSBundle mainBundle] resourcePath]] getCString:buffer maxLength:200 encoding:NSASCIIStringEncoding];
result = system->createSound(buffer, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound9);
ERRCHECK(result);
- (IBAction)playSound1:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound1, false, &wob01);
ERRCHECK(result);
- (IBAction)stopSound1:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob01->stop();
ERRCHECK(result);
- (IBAction)playSound2:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound2, false, &wob02);
ERRCHECK(result);
- (IBAction)stopSound2:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob02->stop();
ERRCHECK(result);
- (IBAction)playSound3:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound3, false, &wob03);
ERRCHECK(result);
- (IBAction)stopSound3:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob03->stop();
ERRCHECK(result);
- (IBAction)playSound4:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound4, false, &wob04);
ERRCHECK(result);
- (IBAction)stopSound4:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob04->stop();
ERRCHECK(result);
- (IBAction)playSound5:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound5, false, &wob05);
ERRCHECK(result);
- (IBAction)stopSound5:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob05->stop();
ERRCHECK(result);
- (IBAction)playSound6:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound6, false, &wob06);
ERRCHECK(result);
- (IBAction)stopSound6:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob06->stop();
ERRCHECK(result);
- (IBAction)playSound7:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound7, false, &wob07);
ERRCHECK(result);
- (IBAction)stopSound7:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob07->stop();
ERRCHECK(result);
- (IBAction)playSound8:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound8, false, &wob08);
ERRCHECK(result);
- (IBAction)stopSound8:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob08->stop();
ERRCHECK(result);
- (IBAction)playSound9:(id)sender
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound9, false, &wob09);
ERRCHECK(result);
- (IBAction)stopSound9:(id)sender
FMOD_RESULT result = FMOD_OK;
result = wob09->stop();
ERRCHECK(result);
如您所见,所有代码都是重复的。这是我能够让它工作的唯一方法,但我知道这些可以放入一个数组中,我就是想不通。可能是一个 NSMutableArray 并列出“sound1”、“sound2”等。然后在界面生成器中为每个按钮分配一个标签?理想情况下,我想要一个用于 stopSound 的函数,一个用于 playSound 等,它使用标签来播放或停止正确的声音文件。使用 FMOD 的 system->createSound() 时,最后一个参数是一个变量,用于存储新创建的声音。有没有办法将其存储在数组或字典中?如果是这样,我想不通。
任何建议都将不胜感激。我不想再为这个简单的问题而苦恼了。
谢谢!
【问题讨论】:
这些“wob”变量是什么? “Wob”变量只是 FMOD 通道。这有意义吗? 【参考方案1】:我会将声音包装到NSObject
的子类中,并使其成为一个独立的单元。一个声音会有像play
、stop
、pause
这样的操作,以及像isPlaying
这样的访问器。
然后,为了使其更加通用,我将搜索与模式 "*.wav"
匹配的所有文件,然后为每个匹配的文件名,使用该文件名初始化 Sound
对象,并将其添加到数组中。
这是我想象的 Sound 对象的样子:
@interface Sound : NSObject
@property FMOD::Sound *sound;
- (id)initWithSoundFilePath:(NSString *)path;
- (void)play;
- (void)stop;
@end
@implementation Sound
- (void)dealloc
// free the memory occupied by the sound pointer here
- (id)initWithSoundFilePath:(NSString *)path
self = [super init];
if (self)
result = system->createSound(path, FMOD_SOFTWARE | FMOD_LOOP_NORMAL, NULL, &sound);
ERRCHECK(result);
return self;
- (void)play
FMOD_RESULT result = FMOD_OK;
result = system->playSound(FMOD_CHANNEL_FREE, sound, false, /* What is this wob? */);
ERRCHECK(result);
- (void)stop
FMOD_RESULT result = FMOD_OK;
result = /* What is this wob */->stop();
ERRCHECK(result);
@end
所以你有它。声音现在被很好地封装了。我发现这个answer 有助于查找某个目录中符合某些条件的所有文件的列表。您可以在视图控制器中使用它来自动生成所有相关的 Sound 对象并将其添加到数组中。
- (NSArray *)getPathsOfSoundFiles
NSString *rootPath = [[NSBundle mainBundle] resourcePath];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *files = [fm contentsOfDirectoryAtPath:rootPath error:nil];
NSPredicate *soundFileFilter = [NSPredicate predicateWithFormat:@"self ENDSWITH '.wav'"];
NSArray *soundFilePaths = [files filteredArrayUsingPredicate:soundFileFilter];
return soundFilePaths;
好的,现在您可以检索所有 .wav 文件的路径,下一步是在您的 viewWillAppear
或任何最有意义的方法中初始化它们。
- (void)viewWillAppear:(BOOL)animated
NSArray *paths = [self getPathsOfSoundFiles];
NSMutableArray *sounds = [NSMutableArray array];
for (NSString *path in paths)
Sound *sound = [[Sound alloc] initWithSoundFilePath:path];
[sounds addObject:sound];
self.sounds = sounds;
通过声音阵列设置,播放和停止给定的声音变得相当容易。使用可以创建一个将索引放入数组的方法,或者可能是 Sound
对象本身并完成这项工作。
- (void)playSoundAtIndex:(NSUInteger)soundIndex
Sound *sound = [self.sounds objectAtIndex:soundIndex];
[sound play];
- (void)stopSoundAtIndex:(NSUInteger)soundIndex
Sound *sound = [self.sounds objectAtIndex:soundIndex];
[sound stop];
【讨论】:
感谢您的回复。我现在正在查看它,但我想提一下“wob1”等只是频道。因此,使用 FMOD,您可以根据需要将每个声音放入一系列通道中。就是这样。 在这种情况下,您可以将要播放的频道作为初始化的一部分传递给Sound
对象。
我理解这种方法,但是我在实现它时遇到了一些麻烦,因为当我创建一个新的 Sound 对象时,对 FMOD 及其类的任何引用都会引发错误。例如:@property FMOD::Sound *sound;给出这些错误:预期的表达式,缺少类型说明符,默认为“int”,类型名称需要说明符或限定符
似乎无论我是否在 Sound.h 文件中再次包含 FMOD,FMOD::Sound 都不是有效类型。知道为什么吗?我还尝试命名新对象 SoundObj,以防出现命名冲突。我尝试在 Sound.h 文件中声明以下变量: FMOD::System *system; FMOD::声音*声音;但似乎每当我尝试从 FMOD 访问一个类时,都会出现错误。我对此有点陌生,所以它可能很简单。知道为什么这不起作用吗?除此之外,我理解这个解决方案,这正是我想要的!
将您的实现文件扩展名从“.m”重命名为“.mm”,以告诉编译器它包含 C++ 代码。并将类型说明符更改为@property (nonatomic, strong) FMOD::Sound *sound;
。【参考方案2】:
你可以泛化你的代码很多:
使用数组来存储您的声音和“wobs” 使用类似[NSString stringWithFormat:@"%@/sound%i.wav", [[NSBundle mainBundle] resourcePath], index]
的循环
只有一个-playSound:
和一个-stopStound:
操作使用sender
的tag(您可以在Interface Builder 中设置或在视图控制器的代码中创建按钮时自动化)来确定哪个声音播放或停止哪个摆动。
【讨论】:
【参考方案3】:我强烈建议您继承 UIButton 并覆盖触摸方法以在发生这些操作时播放音频文件。当您继承 UIButton 时,您需要在头文件中设置一个属性,以便您可以传入音频文件或音频文件所在位置的字符串,这样就可以轻松地将其动态化。
然后像我说的那样在主文件中覆盖那些触摸方法(touchesBegan、touchesEnded 等)来播放您可以传入我们在头文件中定义的自定义属性。
这就是我至少要解决您的问题的方式,然后您只有一个 UIButton 子类可以根据您传入的音频文件执行完全相同的操作。
【讨论】:
-1 在任何情况下,很少需要继承UIButton
。
-1:这种设计违背了 MVC、目标/动作设计模式以及 UIButton
应该负责的理念。 UIButton
应该负责了解它的目标和相关操作,了解它应该如何布置其子视图,了解它的状态以及如何将其显示到屏幕上,等等。它应该不实现与点击它相关的实际应用程序行为。以上是关于在 Objective-C 中使用数组试图简化大量重复代码的主要内容,如果未能解决你的问题,请参考以下文章
Objective-C:如何从 .NET 的 BitConverter 类中读取数据