iOS - 消息机制

embedded/2025/1/8 18:58:31/

1. 基本数据结构

// 方法结构
struct method_t {SEL name;       // 方法名const char *types;  // 类型编码IMP imp;       // 方法实现
};// 类结构
struct objc_class {Class isa;Class superclass;cache_t cache;        // 方法缓存class_data_bits_t bits;  // 类的方法、属性等信息
};// 方法缓存
struct cache_t {struct bucket_t *_buckets;  // 散列表mask_t _mask;              // 容量掩码mask_t _occupied;          // 已使用数量
};

2. 消息发送流程

2.1 基本流程

// 消息发送入口
id objc_msgSend(id self, SEL _cmd, ...) {if (!self) return nil;// 1. 查找方法缓存IMP imp = cache_getImp(self->isa, _cmd);if (imp) return imp(self, _cmd, ...);// 2. 完整查找流程return _objc_msgSend_uncached(self, _cmd, ...);
}

2.2 方法查找

IMP lookUpImpOrForward(id obj, SEL sel) {Class cls = obj->getIsa();// 1. 查找当前类的方法Method method = class_getInstanceMethod(cls, sel);if (method) {// 加入缓存cache_fill(cls, sel, method->imp);return method->imp;}// 2. 查找父类方法for (Class tcls = cls->superclass; tcls; tcls = tcls->superclass) {method = class_getInstanceMethod(tcls, sel);if (method) {cache_fill(cls, sel, method->imp);return method->imp;}}// 3. 动态方法解析if (resolveInstanceMethod(cls, sel)) {return lookUpImpOrForward(obj, sel);}// 4. 消息转发return _objc_msgForward;
}

3. 方法缓存机制

3.1 缓存结构

struct cache_t {// 缓存桶struct bucket_t {SEL _sel;   // 方法名IMP _imp;   // 方法实现} *_buckets;// 查找方法IMP imp(SEL sel) {bucket_t *b = buckets();mask_t m = mask();mask_t begin = cache_hash(sel, m);mask_t i = begin;do {if (b[i].sel() == sel) {return b[i].imp();}} while ((i = cache_next(i, m)) != begin);return nil;}
};

3.2 缓存优化

// 缓存哈希算法
static inline mask_t cache_hash(SEL sel, mask_t mask) {return (mask_t)(uintptr_t)sel & mask;
}// 缓存扩容
void cache_t::expand() {uint32_t oldCapacity = capacity();uint32_t newCapacity = oldCapacity ? oldCapacity * 2 : INIT_CACHE_SIZE;if (newCapacity > MAX_CACHE_SIZE) {newCapacity = MAX_CACHE_SIZE;}reallocate(oldCapacity, newCapacity);
}

4. 消息转发机制

4.1 动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)sel {if (sel == @selector(someMethod:)) {class_addMethod(self,sel,(IMP)dynamicMethodIMP,"v@:");return YES;}return [super resolveInstanceMethod:sel];
}

4.2 快速转发

- (id)forwardingTargetForSelector:(SEL)aSelector {if (aSelector == @selector(someMethod:)) {return alternateObject;  // 转发给其他对象}return [super forwardingTargetForSelector:aSelector];
}

4.3 完整转发

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {if (aSelector == @selector(someMethod:)) {return [NSMethodSignature signatureWithObjCTypes:"v@:"];}return [super methodSignatureForSelector:aSelector];
}- (void)forwardInvocation:(NSInvocation *)anInvocation {if ([alternateObject respondsToSelector:[anInvocation selector]]) {[anInvocation invokeWithTarget:alternateObject];} else {[super forwardInvocation:anInvocation];}
}

5. 性能优化

5.1 方法缓存

// 缓存命中检查
static ALWAYS_INLINE IMP cache_getImp(Class cls, SEL sel) {cache_key_t key = cache_key(sel);bucket_t *buckets = cls->cache.buckets();mask_t mask = cls->cache.mask();bucket_t *bucket = &buckets[key & mask];if (bucket->key() == key) {return bucket->imp();}return nil;
}

5.2 方法内联

// 编译器优化
static ALWAYS_INLINE id 
objc_msgSendSuper2(struct objc_super2 *super, SEL op, ...) {return ((id (*)(struct objc_super2 *, SEL, ...))objc_msgSendSuper2_fixup)(super, op, ...);
}

6. 特殊情况处理

6.1 nil 消息

if (!self) return nil;  // 发送给 nil 的消息返回 nil

6.2 Tagged Pointer

if (objc_isTaggedPointer(obj)) {// 特殊处理 Tagged Pointerreturn objc_msgSend_tagged(obj, sel, ...);
}

7. 线程安全

7.1 缓存更新

static void cache_fill(Class cls, SEL sel, IMP imp) {cache_t *cache = &cls->cache;cache->mutex.lock();// 更新缓存cache->insert(sel, imp);cache->mutex.unlock();
}

7.2 方法添加

static IMP addMethod(Class cls, SEL name, IMP imp, const char *types) {mutex_locker_t lock(runtimeLock);IMP result = nil;if (addMethodNoLock(cls, name, imp, types, &result)) {// 更新方法缓存flushCaches(cls);}return result;
}

这个消息机制设计的关键点:

  1. 高效的方法查找
  2. 灵活的消息转发
  3. 优秀的缓存策略
  4. 完善的线程安全
  5. 特殊情况的处理

这些特性使得 Objective-C 的消息机制既灵活又高效。


http://www.ppmy.cn/embedded/152351.html

相关文章

各知名云平台对于 MySQL TDE的支持汇总和对比

MySQL Community TDE功能介绍: TDE: Transparent Data Encryption, 也就是透明传输加密。在MySQL community官网上有这样原文介绍:With MySQL version 5.7.12 and up, Oracle continues to improve MySQL’s security features by adding MySQL Enterpr…

vulnhub靶场-potato(至获取shell)

arp-scan -l 扫描IP 使用御剑端口扫描扫描端口,扫到了80和7120两个端口,其中7120为ssh端口 使用dirb http://192.168.171.134 扫描目录 发现info.php 访问为phpinfo界面 访问192.168.171.134为一个大土豆,没什么用 所以我们从ssh入手 盲…

Couchbase 和数据湖技术的区别、联系和相关性分析

Couchbase 和数据湖技术(如 Delta Lake、Apache Hudi、Apache Iceberg)分别是两类不同的数据存储与管理系统,但它们也可以在特定场景中结合使用,以下是它们的区别、联系和相关性分析: 区别: 1. 核心用途&a…

Docker: 教程07 - ( 如何对 Docker 进行降级和升级)

如果我们使用 docker 来管理容器,那么保持 docker 引擎的更新将会是十分重要的,这一篇文章我们将会讨论如何对Docker 进行降级和升级。 准备工作 - docker 环境 我们需要拥有一个安装好 docker 的运行环境。 如果你需要了解如何安装 docker 可以通过如…

Go语言的数据库交互

Go语言的数据库交互 引言 在现代软件开发中,数据库是应用程序不可或缺的组成部分。无论是处理用户数据、管理资产信息,还是记录日志,数据库都发挥着重要作用。Go语言以其简洁的语法和优异的并发性能,逐渐成为开发高效、可扩展应…

iOS - 线程与AutoreleasePoolPage

1. AutoreleasePoolPage 结构 struct AutoreleasePoolPage {static pthread_key_t key; // 只占用一份内存空间magic_t const magic;id *next;pthread_t const thread;AutoreleasePoolPage *parent;AutoreleasePoolPage *child;// ...其他成员变量 } pthread 的 TLS (Th…

博客标题:使用Go和RabbitMQ构建高效的消息队列系统

引言 在现代分布式系统中,消息队列扮演着至关重要的角色。它们允许不同组件之间异步通信,从而提高系统的可扩展性和可靠性。今天我们将探讨如何利用Go语言和流行的开源消息代理RabbitMQ来创建一个简单但功能强大的消息队列系统。 环境准备 为了确保你能够…

如何二次封装组件(vue3版本)

在开发 Vue 项目中我们一般使用第三方组件库进行开发,如 Element-Plus, 但是这些组件库提供的组件并不一定满足我们的需求,这时我们可以通过对组件库的组件进行二次封装,来满足我们特殊的需求。 对于封装组件有一个大原则就是我们应该尽量保…