dispatch_once优化代码性能

本文介绍如何使用dispatch_once优化变量初始化过程,确保仅调用API一次,并阐述了单例模式的实现方法及其在Objective-C中的应用。讨论了dispatch_once的优势,如线程安全性与简化代码,同时对比了共享实例与非共享实例的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有些变量只需要初始化一次(如从文件中读取配置参数,读取设备型号等等),可以使用dispatch_once来进行读取优化,保证只调用API一次,以后就只要直接访问变量即可

范例如下:

static BOOL isTestMode;

+ (BOOL)isTestMode
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSNumber* obj = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFResourceTest"];
        isTestMode = [obj boolValue];
    });
   
    return isTestMode;
}

无论是爱还是恨,你都需要单例。实际上每个iOS或Mac OS应用都至少会有 UIApplicationNSApplication.
什么是单例呢?Wikipedia是如此定义的:
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
或者我的理解是:
单例是一种类,该类只能实例化一个对象。
    尽管这是单例的实际定义,但在Foundation框架中不一定是这样。比如 NSFileMangerNSNotificationCenter,分别通过它们的类方法 defaultManagerdefaultCenter获取。尽管不是严格意义的单例,这些类方法返回一个可以在应用的所有代码中访问到的类的共享实例。在本文中我们也会采用该方法。
    使用Objective-C实现单例模式的最佳方式向来有很多争论,开发者(包括Apple在内)似乎每几年就会改变他们的想法。当Apple引入了 Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他们也引入了一个很适合用于实现单例模式的函数。
    该函数就是 dispatch_once
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
    该函数接收一个 dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为 BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块,对于本例就用于shared实例的实例化。
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如 @synchronized之类的来防止使用多个线程或者队列时不同步的问题。
    Apple的 GCD Documentation证实了这一点:
如果被多个线程调用,该函数会同步等等直至代码块完成。
    实际要如何使用这些呢?
    好吧,假设有一个 AccountManager类,你想在整个应用中访问该类的共享实例。你可以按如下代码简单实现一个类方法:
+ (AccountManager *)sharedManager { 
    static AccountManager *sharedAccountManagerInstance = nil; 
 static dispatch_once_t predicate;
 dispatch_once(&predicate, ^{       
          sharedAccountManagerInstance = [[self alloc] init]; 
     } );
    return sharedAccountManagerInstance; 
}
    这就意味着你任何时候访问共享实例,需要做的仅是:
AccountManager *accountManager = [AccountManager sharedManager];
    就这些,你现在在应用中就有一个共享的实例,该实例只会被创建一次。
    该方法有很多优势: 
           1 线程安全
           2 很好满足静态分析器要求
           3 和自动引用计数(ARC)兼容 
           4 仅需要少量代码
        该方法的 劣势 就是它仍然运行创建一个非共享的实例:
    AccountManager *accountManager = [[AccountManager alloc] init];
        有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。
     

    D:\PythonProject\.venv\Scripts\python.exe D:\PythonProject\app.py * Serving Flask app 'app' * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on http://127.0.0.1:5000 Press CTRL+C to quit * Restarting with stat * Debugger is active! * Debugger PIN: 120-624-777 This runs only once on the first request! 127.0.0.1 - - [10/Jun/2025 16:50:28] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [10/Jun/2025 16:50:28] "GET /static/css/style.css HTTP/1.1" 404 - 127.0.0.1 - - [10/Jun/2025 16:50:28] "GET /static/js/main.js HTTP/1.1" 404 - 127.0.0.1 - - [10/Jun/2025 16:50:30] "GET /doctor_list?department=1&date=2025-06-10 HTTP/1.1" 500 - Traceback (most recent call last): File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 1536, in __call__ return self.wsgi_app(environ, start_response) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 1514, in wsgi_app response = self.handle_exception(e) File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 1511, in wsgi_app response = self.full_dispatch_request() File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 919, in full_dispatch_request rv = self.handle_user_exception(e) File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 917, in full_dispatch_request rv = self.dispatch_request() File "D:\PythonProject\.venv\Lib\site-packages\flask\app.py", line 902, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ File "D:\PythonProject\app.py", line 64, in doctor_list flash(f'ID为 {department_id} 的科室不存在', 'warning') ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\PythonProject\.venv\Lib\site-packages\flask\helpers.py", line 335, in flash session["_flashes"] = flashes ~~~~~~~^^^^^^^^^^^^ File "D:\PythonProject\.venv\Lib\site-packages\flask\sessions.py", line 104, in _fail raise RuntimeError( ^ RuntimeError: The session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret. 127.0.0.1 - - [10/Jun/2025 16:50:30] "GET /doctor_list?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 304 - 127.0.0.1 - - [10/Jun/2025 16:50:30] "GET /doctor_list?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 - 127.0.0.1 - - [10/Jun/2025 16:50:30] "GET /doctor_list?__debugger__=yes&cmd=resource&f=console.png&s=v8WikeQeBL2PiowtaTJt HTTP/1.1" 200 -
    最新发布
    06-11
    评论 2
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包

    打赏作者

    小涵

    你的鼓励将是我创作的最大动力

    ¥1 ¥2 ¥4 ¥6 ¥10 ¥20
    扫码支付:¥1
    获取中
    扫码支付

    您的余额不足,请更换扫码支付或充值

    打赏作者

    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值