第七讲:Obj-C 协议 Protocol

本文详细介绍了Objective-C中的协议概念,包括如何定义协议、如何使用协议,以及协议与接口的实现方式。文中还提供了简单的示例代码,展示了如何通过respondsToSelector方法来判断对象是否响应特定方法。

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

转:http://tigercat1977.blog.163.com/blog/static/2141561122012111294949110/

第七讲:Obj-C 协议 Protocol 

2012-12-12 09:49:49|  分类: Objective-C |  标签:objective-c  |字号 订阅

主要内容
      什么是协议
      如何定义协议
      如何使用协议

什么是协议
     协议是多个对象之间协商的一个接口对象
     协议提供一系列方法用来在于协议的实现者和代理者之间的一种通信方式
     协议类似于 C++ 中的纯虚函数,或者 java 中的接口

协议声明
      头文件 Foo.h
     @pritocol MyProtocol <NSObject>
              // 要定义的变量
              - (void) init;
              - (int) update:(int) time;
      @end


协议
      Protocol 声明一系列方法,这些放在实现 Protocol 中实现
      协议方法可以实现 optional , 可选实现
      @required     // 某一个方法必须实现
      @optional    // 某个方法可以不去实现

      协议需要继承于 基协议 NSObject
      协议可以多继承


协议声明
      头文件 Foo.h
     @pritocol MyProtocol <NSObject>
              // 要定义的变量
      @optional   // 可选实现
              - (void) init;
              - (void) exit;
      @required  // 必须实现
              - (int) update:(int) time;
      @end


OC 定义协议和实现协议
      头文件 Foo.h
      @protocol MouseListener
      @optional
              - (BOOL) mousePressed;
              - (BOOL) mouseClicked;
      @end
      @protocol KeyboardListener
              - (BOOL) keyPressed;
      @end

      @interface Foo: NSObject <MouseListener, KeyboardListener>
      {
      ......
      }
      @end

一个简单协议例子
   第七讲:Obj-C 协议 Protocol - tigercat1977 - tiger notes

协议使用例子

第七讲:Obj-C 协议 Protocol - tigercat1977 - tiger notes

判断某个对象是否响应方法
      既然接口可以部分实现,OC 对象提供了动态判断某个方法是否实现的方法
      respondsToSelector
      if ([test respondsToSelector:@selector(showInfo )])
      {
            [test  showInfo];
      }

respondsToSelector

第七讲:Obj-C 协议 Protocol - tigercat1977 - tiger notes


转载于:https://www.cnblogs.com/jackljf/archive/2013/03/07/3589249.html

(gdb) bt #0 _assert (filename=filename@entry=0x3dc9ca08 "mm_heap/mm_free.c", linenum=linenum@entry=169, msg=msg@entry=0x0, regs=0x3ddbba70 <g_last_regs>, regs@entry=0x0) at misc/assert.c:605 #1 0x1db0fc66 in __assert (filename=filename@entry=0x3dc9ca08 "mm_heap/mm_free.c", linenum=linenum@entry=169, msg=msg@entry=0x0) at assert/lib_assert.c:36 #2 0x1db1a3fa in mm_delayfree (delay=false, mem=0x3de77c20, heap=0x3dde64e0) at mm_heap/mm_free.c:169 #3 mm_delayfree (delay=false, mem=0x3de77c20, heap=0x3dde64e0) at mm_heap/mm_free.c:73 #4 mm_free (heap=0x3dde64e0, mem=0x3de77c20) at mm_heap/mm_free.c:264 #5 0x1db1af5e in free (mem=<optimized out>) at umm_heap/umm_free.c:49 #6 0x1dc7209a in av_free (ptr=<optimized out>) at ffmpeg/libavutil/mem.c:251 #7 0x1dc3aa76 in ffio_rewind_with_probe_data (s=s@entry=0x3de4b100, bufp=bufp@entry=0x3de444d8, buf_size=<optimized out>, buf_size@entry=4096) at ffmpeg/libavformat/aviobuf.c:1213 #8 0x1dc44136 in av_probe_input_buffer2 (pb=0x3de4b100, fmt=fmt@entry=0x3de457a4, filename=filename@entry=0x3de44608 "http://iot102.music.126.net/Yml6PWlvdCZjaGFubmVsPXhpYW9taS12ZWxhJnNjZW5lPWFuZHJ3ZWFy/20250708132631/a7a9136a55a5a24372b85f011aaf6f62/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/34566871844/2a6b/12ff/c278/14fb9b"..., logctx=<error reading variable: Unable to access DWARF register number 80>, logctx@entry=0x3de457a0, offset=0, max_probe_size=524288) at ffmpeg/libavformat/format.c:309 #9 0x1dc3da9c in init_input (options=0x3de4452c, filename=0x3de44608 "http://iot102.music.126.net/Yml6PWlvdCZjaGFubmVsPXhpYW9taS12ZWxhJnNjZW5lPWFuZHJ3ZWFy/20250708132631/a7a9136a55a5a24372b85f011aaf6f62/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/34566871844/2a6b/12ff/c278/14fb9b"..., s=0x3de457a0) at ffmpeg/libavformat/demux.c:178 #10 avformat_open_input (ps=ps@entry=0x3de0a864, filename=filename@entry=0x3de44608 "http://iot102.music.126.net/Yml6PWlvdCZjaGFubmVsPXhpYW9taS12ZWxhJnNjZW5lPWFuZHJ3ZWFy/20250708132631/a7a9136a55a5a24372b85f011aaf6f62/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/34566871844/2a6b/12ff/c278/14fb9b"..., fmt=fmt@entry=0x0, options=options@entry=0x3de44580) at ffmpeg/libavformat/demux.c:291 #11 0x1dc351b6 in movie_async_open_demuxer (ctx=ctx@entry=0x3de0a7a0, filename=filename@entry=0x3de09a48 "http://iot102.music.126.net/Yml6PWlvdCZjaGFubmVsPXhpYW9taS12ZWxhJnNjZW5lPWFuZHJ3ZWFy/20250708132631/a7a9136a55a5a24372b85f011aaf6f62/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/34566871844/2a6b/12ff/c278/14fb9b"...) at ffmpeg/libavfilter/src_movie_async.c:478 #12 0x1dc35930 in movie_async_prepare (filename=<optimized out>, ctx=<optimized out>) at ffmpeg/libavfilter/src_movie_async.c:556 #13 movie_async_proc_cmd (msg=0x3de09a40, ctx=0x3de0a7a0) at ffmpeg/libavfilter/src_movie_async.c:928 #14 movie_async_thread (arg=0x3de0a7a0, arg@entry=<error reading variable: value has been optimized out>) at ffmpeg/libavfilter/src_movie_async.c:990 #15 0x1db11eb8 in pthread_startup (entry=<optimized out>, arg=<optimized out>) at pthread/pthread_create.c:59 --Type <RET> for more, q to quit, c to continue without paging--frame 0 #16 0x1db02778 in pthread_start () at pthread/pthread_create.c:139 #17 0x00000000 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)
07-17
<think>我们正在处理一个GDB堆栈跟踪中显示的内存释放错误,具体是在mm_free.c的第169行出现断言失败,发生在FFmpeg的avformat_open_input函数中释放内存时。根据引用[1]中提供的代码片段,我们可以看到avformat_open_input最终会调用io_open_default函数,该函数处理打开输入的操作。引用[2]则提供了一些关于流和编解码器参数的信息,这可能与输入文件的格式有关。 首先,我们需要理解错误发生的上下文。mm_free.c中的断言失败通常与内存管理错误有关,比如双重释放、内存损坏或释放了未分配的指针等。由于错误发生在avformat_open_input函数中,这个函数用于打开媒体文件并读取头部信息,因此问题可能与输入文件的处理有关。 ### 问题分析步骤 1. **理解堆栈跟踪**:我们需要完整的GDB堆栈跟踪信息,以确定从avformat_open_input到mm_free.c第169行的调用路径。这有助于我们定位是哪个内存块导致了问题。 2. **检查输入文件**:avformat_open_input函数会解析输入文件。如果输入文件损坏或格式异常,可能会导致内存分配/释放出现问题。尝试使用其他文件或工具验证输入文件的正确性。 3. **内存调试工具**:使用Valgrind(特别是Memcheck工具)来检测内存错误。Valgrind可以帮助发现内存泄漏、非法内存访问等问题。 4. **检查FFmpeg版本**:不同版本的FFmpeg可能有不同的内存管理行为。确保使用的FFmpeg版本是稳定的,或者查看是否有已知的相关问题。 5. **分析mm_free.c的代码**:虽然我们没有mm_free.c的具体代码,但通常这类错误发生在释放内存时,内存分配器检测到堆结构被破坏(例如,在分配区域之前或之后发生了写越界)。 ### 可能的原因及解决方案 - **双重释放**:同一块内存被释放两次。检查代码中是否有重复释放资源的地方,特别是在错误处理路径中。在FFmpeg中,资源清理通常由特定函数(如avformat_close_input)负责,不应手动释放内部结构。 - **堆溢出**:在内存分配区域之外进行了写入操作,导致分配器元数据被破坏。使用Valgrind检查是否有写越界。 - **使用已释放内存**:如果释放了内存但后续又使用了它,也可能导致分配器在后续操作中检测到不一致。 - **多线程问题**:如果在多线程环境中使用FFmpeg,需要确保正确同步。FFmpeg的某些函数不是线程安全的。 ### 调试建议 1. 使用GDB获取更详细的堆栈信息,包括函数参数和局部变量。例如,在mm_free.c的断言失败处,检查要释放的指针值以及该指针相关的内存块信息。 2. 在Valgrind下运行程序: ```bash valgrind --tool=memcheck --leak-check=full ./your_program ``` 3. 如果问题可重现,尝试简化输入(例如,使用一个小的、已知正确的媒体文件)来排除文件损坏的问题。 4. 检查自定义的I/O回调(如果使用了的话)。引用[1]中的代码显示,如果设置了`open_cb`,则会使用自定义的回调。自定义I/O回调如果实现不当,可能导致内存问题。 5. 更新FFmpeg到最新版本,或查看相关问题的修复补丁。 ### 相关问题 1. 如何获取完整的GDB堆栈跟踪? 2. 如何使用Valgrind检测FFmpeg中的内存错误? 3. 在FFmpeg中,avformat_open_input函数内部的内存管理机制是怎样的? 4. 如何避免在FFmpeg中发生内存双重释放? 5. FFmpeg中自定义I/O回调的正确实现方式是怎样的? [^1]: 引用自提供的代码片段。</think>### GDB堆栈跟踪中内存释放错误分析 (mm_free.c:169) 当在FFmpeg的`avformat_open_input`函数中释放内存时触发`mm_free.c`第169行的断言失败,通常表明内存管理子系统检测到严重错误。结合引用[1]中`io_open_default`的代码路径和引用[2]的编解码器参数信息,以下是系统级分析: #### 错误本质 断言失败发生在`mm_free.c:169`,表明内存分配器在释放操作时检测到以下问题之一: 1. **双重释放**:尝试释放已释放的内存块 2. **堆溢出**:内存块头部元数据被破坏(如越界写入) 3. **非法指针**:传入非分配器分配的指针 4. **内存池污染**:不同内存池间的交叉释放 #### 关键分析路径 ```mermaid graph TD A[avformat_open_input] --> B[io_open_default] B --> C{使用回调?} C ----> D[自定义open_cb] C ----> E[ffio_open_whitelist] E --> F[协议层内存分配] F -->|错误指针| G[mm_free.c 释放] G -->|断言失败| H[mm_free.c:169] ``` #### 可能原因及解决方案 1. **回调函数内存管理问题**(参考引用[1]) - 📌 若启用了`open_cb`自定义回调: ```c if (s->open_cb) return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options); ``` - ✅ **检查点**:确认回调函数是否: - 返回了非`ffio_open_whitelist`分配的指针 - 修改了`AVIOContext`的生命周期管理 - 错误释放了协议层内部指针 2. **协议白名单/黑名单处理缺陷**(引用[1]末行) ```c ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist); ``` - 🚨 风险点:非常规协议处理可能导致: - 未初始化的`pb`指针被二次释放 - 协议层返回野指针(如网络协议超时后释放资源) 3. **编解码器参数污染**(参考引用[2]) ```c st->codecpar->codec_id = types->codec_id; // 可能触发后续内存分配 ``` - 🔍 关联分析:当`codec_id`设置为`AV_CODEC_ID_H264`时: - 检查H264解析器是否在`avformat_open_input`期间提前释放NALU缓冲区 - 验证`AVCodecParameters`结构是否被多线程访问 4. **内存调试工具链** - **立即行动**: ```bash # 使用Valgrind检测非法操作 valgrind --tool=memcheck --track-origins=yes ./your_ffmpeg_app # GDB中断点设置 b mm_free.c:169 if ((size_t)ptr == 0xBAD_POINTER) # 替换实际崩溃指针 ``` - 📊 关键检查项: | 检查项 | 命令 | 预期结果 | |-------|------|---------| | 指针有效性 | `p *(mchunkptr)ptr` | 元数据未损坏 | | 分配大小 | `p ((mchunkptr)ptr)->size & ~0x7` | 匹配申请大小 | | 相邻块 | `p ((mchunkptr)ptr)->fd` | 指向有效内存 | #### 修复建议 1. **升级基础库**(参考引用[3]) - 确保GLIBCXX版本一致性:`strings /usr/lib/libstdc++.so.6 | grep GLIBCXX` 2. **隔离复现环境** ```bash # 使用FFmpeg内存调试选项 export MALLOC_CHECK_=3 export MALLOC_PERTURB_=$(echo $RANDOM % 255 + 1) ``` 3. **代码级修复** ```diff // 在io_open_default中增加保护 + if (*pb && !(*pb)->buffer) { + av_log(s, AV_LOG_WARNING, "Suspicious AVIOContext detected"); + return AVERROR(ENOMEM); + } ``` ### 相关问题 1. 如何验证FFmpeg中的内存分配器是否被第三方库替换? 2. 在自定义I/O回调中如何正确管理`AVIOContext`的生命周期? 3. 当遇到`mm_free.c`断言失败时,如何通过核心转储文件定位首次内存损坏位置? 4. FFmpeg协议层处理非常规URL时有哪些常见的内存陷阱? 5. 如何为FFmpeg编译带完整内存调试符号的版本?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值