DISPATCH_SOURCE_TYPE_TIMER那些容易坑的点

本文介绍了一个基于 DISPATCH 框架的定时器实现方法,并详细解释了如何创建和配置 DISPATCH 类型的定时器。同时,文章强调了在多线程环境下使用定时器时需要注意的问题,例如避免计数错误和崩溃等问题。

timer使用代码类似如下:

    dispatch_source_t timer;
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC),
                              5*NSEC_PER_SEC,
                              0);
    dispatch_source_set_event_handler(timer, ^{
        dispatch_suspend(timer);
    });
    dispatch_resume(timer);

注意:

1 这个timer是使用引用计数的,千万别多线程共用一个timer,容易操作出问题,随机crash;

2 这个timer是使用引用计数的, dispatch_resume不能调用多了,调2次会crash;

3 这个timer是使用引用计数的,dispatch_suspend不能调多了,调多了,计数会有问题;

4 前后台相关;



iOS 崩溃日志中的 `SIGSEGV` 错误通常表示程序试图访问未分配给它的内存地址,或者访问了受保护的内存区域。当崩溃日志中出现 `SEGV_ACCERR` 并指向 `libdispatch.dylib` 时,这通常与多线程操作中的内存访问问题有关,尤其是在使用 GCD(Grand Central Dispatch)进行异步任务调度时。 ### 崩溃日志结构分析 崩溃日志通常包含以下几个关键部分: - **Code Type**: 指出崩溃时的 CPU 架构,例如 `ARM-64`。 - **OS Version**: 显示崩溃发生的系统版本,例如 `iOS 10.2 (14C82)`。 - **Binary Images**: 显示崩溃时加载的二进制文件及其地址范围。 - **Thread**: 显示崩溃发生时的线程堆栈信息。 - **Exception Type**: 显示异常类型,如 `SIGSEGV`。 - **Exception Codes**: 显示异常的具体代码,如 `SEGV_ACCERR`。 ### SIGSEGV SEGV_ACCERR 的常见原因 `SIGSEGV` 是一个段错误信号,表示程序访问了非法内存地址。`SEGV_ACCERR` 则表示尝试访问内存时权限足,通常是由于以下几种原因: - **访问已释放的对象**:在 ARC(自动引用计数)环境下,如果一个对象已经被释放,但仍有代码试图访问它,就会导致段错误。 - **多线程访问冲突**:在多线程环境中,如果多个线程同时访问并修改同一个对象,而没有适当的同步机制,可能会导致内存访问冲突。 - **使用 GCD 时的错误**:在使用 GCD 的 `dispatch_async` 或其他异步函数时,如果传递的对象在异步任务执行前被释放,可能会导致段错误。 ### 分析崩溃日志的步骤 1. **查看崩溃线程的堆栈信息**:找到崩溃发生时的线程堆栈信息,确定是哪个函数或方法导致了崩溃。 2. **检查异常类型和代码**:确认崩溃是否由 `SIGSEGV` 和 `SEGV_ACCERR` 引起。 3. **定位内存访问问题**:通过堆栈信息,检查是否有访问已释放对象或未初始化指针的情况。 4. **使用 dSYM 文件解析日志**:通过 `symbolicatecrash` 工具将地址转换为具体的代码行号,便于定位问题。 ### 示例解析 假设崩溃日志中有如下信息: ``` Thread 0 name: Dispatch queue: com.apple.main-thread Thread 0 Crashed: 0 libdispatch.dylib 0x0000000102a40000 _dispatch_call_block_and_release + 24 1 libdispatch.dylib 0x0000000102a40000 _dispatch_client_callout + 16 2 libdispatch.dylib 0x0000000102a40000 _dispatch_main_queue_callback_4CF + 1844 3 CoreFoundation 0x0000000180a40000 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12 4 CoreFoundation 0x0000000180a40000 __CFRunLoopRun + 1660 5 CoreFoundation 0x0000000180a40000 CFRunLoopRunSpecific + 436 6 GraphicsServices 0x0000000182340000 GSEventRunModal + 100 7 UIKit 0x0000000185a40000 UIApplicationMain + 208 8 TestBacktrace 0x0000000102a40000 main + 148 9 libdyld.dylib 0x0000000180a40000 start + 4 ``` 上述日志显示崩溃发生在 `libdispatch.dylib` 的 `_dispatch_call_block_and_release` 函数中,说明问题可能出在某个 GCD 任务的执行过程中。 ### 解决方案 1. **检查对象的生命周期**:确保在 GCD 任务中使用的对象在其生命周期内有效,避免在任务执行前释放对象。 2. **使用弱引用**:在 GCD 任务中使用 `__weak` 引用外部对象,防止循环引用导致内存泄漏或提前释放。 3. **同步机制**:在多线程环境中,使用适当的同步机制(如 `@synchronized`、`NSLock`、`dispatch_semaphore` 等)来保护共享资源的访问。 ### 示例代码修复 ```objective-c - (void)someMethod { __weak typeof(self) weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf) { // 安全地使用 strongSelf [strongSelf doSomething]; } }); } ``` 在上述代码中,使用 `__weak` 引用来避免循环引用,并在 GCD 任务中使用 `__strong` 引用确保对象在任务执行期间有效。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值