[摘要]本文是對音效工具類的封裝的講解,對學習IOS蘋果軟件開發有所幫助,與大家分享。
分析
音效工具類的職責與便利性
- 能夠記錄音效文件的SoundID,無需每一次播報音效的時候都需要重新獲取
能夠對存放在系統中的SoundID進行緩存優化處理
與單例類的比較
- 單利類的話,一旦我們第一次初始化之後,將不會被釋放,對於緩存的優化處理不友好
類方法(靜態方法),無需初始化對象,每一個類在編譯的時候編譯系統都會開辟對於的內存,性能要稍微優於單利類
類方法的特點
- 不能在類方法中調用
self.
獲取對應的屬性
類方法中既不能用屬性也不用成員變量,但是可以用全局靜態變量
代碼
#import <Foundation/Foundation.h>
@interface SPPSoundPlayManager : NSObject
/**
播放音效
@param name 聲音文件名(帶後綴)
@param alert 是否振動
*/
+ (void)playSoundWithName:(NSString *)name alert:(BOOL)alert;
//清除緩存
+ (void)clearMemory;
@end
#import "SPPSoundPlayManager.h"
#import <AVFoundation/AVFoundation.h>
//全局的靜態變量(static修飾符:靜態變量,只能分配一次內存,即只能初始化一次,只會開辟一次內存空間,不會反復重新加載內存)
//音效緩存字典(key: 文件名name, value: soundID)
static NSMutableDictionary *cacheDict;
@interface SPPSoundPlayManager()
//靜態方法中是無法訪問屬性的,改用C的靜態變量
//@property(nonatomic, strong) NSMutableDictionary *cacheDict;
@end
@implementation SPPSoundPlayManager
//第一次加載當前類(類被加載到內存中)的時候會先執行load方法(1. 只會執行一次 2. 一旦你創建了這一個文件就會在編譯時調用,與你是否使用它無關)
+ (void)load {
NSLog(@"load");
}
//第一次使用當前類的時候會執行(1. 只會執行一次 2. 你使用了這個類就會執行,不使用則不執行)
+ (void)initialize {
NSLog(@"initialize"); //類的生命周期
//初始化緩存字典
cacheDict = [NSMutableDictionary dictionary];
}
/**
播放系統音效
@param name 音效的文件名(帶後綴)
@param alert 是否振動
*/
+ (void)playSoundWithName:(NSString *)name alert:(BOOL)alert {
/**
1. 以name為Key, SoundID為Value
2. 先判斷緩存字典對一個的Value是否有值 --> soundID == 0 就代表沒有值/沒有緩存
3. 沒有值就創建, 有的話直接播放
*/
//1. 創建系統音效soundID
SystemSoundID soundid = [cacheDict[name] unsignedIntValue];
//2. 先檢查該文件是否在當前緩存文件中(為0代表沒有在緩存中,那就去創建)
if (soundid == 0) {
//1. 創建音效類
//1.1 獲取包中音效文件的url路徑
NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:nil];
//1.2 音效的播放是由系統底層來播放(基於C的接口)
///傳入音頻路徑url就會和soundID進行綁定,之後需要播放的時候, 只需要調用soundID, 就能找到對應的URL地址
///這樣做的好處就是soundID對音效有一個緩存,當音效文件第一次加載時,系統會將音效文件隨機分配一個soundID,可以在下次播放時直接使用soundID播放,而無需再次獲取對應的url地址
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundid);
//1.3 將文件名與soundid加入到緩存字典中
[cacheDict setValue:@(soundid) forKey:name];
}
//3. 播放音效(音效: 不超過30s的短音樂)
///播放音效有兩種類型,一種是帶振動(必須要真機),一種不帶振動
if (alert) {
//帶振動
AudioServicesPlayAlertSound(soundid);
}else {
//不帶振動
AudioServicesPlaySystemSound(soundid);
}
}
+ (void)clearMemory {
//1. 靜態變量字典在ARC下面釋放內存不能直接設置為nil,應該先清除內部對象
[cacheDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
//2. 清除靜態字典內部的緩存
///ARC只對OC對象有效,對C對象無效(這就是為什麼C對象至今仍保留了retain和release方法)
//2.1 獲取緩存的soundID
SystemSoundID soundID = [obj unsignedIntValue];
//2.2 C對象要釋放一般使用CFRelease(),注意是釋放C對象(堆裡),而soundID是UInt32類型(棧裡),所以看出有一些特殊的系統類內部會給出單獨的API用於釋放,此時應優先使用系統給出的單獨的API,而不能一味套用CFRelease()
//Dispose: 處理int類型的棧內存不能直接使用release
AudioServicesDisposeSystemSoundID(soundID);
}];
//3. 移除字典所有元素
[cacheDict removeAllObjects];
}
@end