Swift Actor报错:向实例发送未识别选择器问题求助
问题分析与解决方案
错误原因解析
你遇到的-[NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance 0x8000000000000000错误,本质是把NSTaggedPointerString(苹果优化的轻量字符串类型)当成NSDictionary调用了objectForKey:方法,而该字符串类型并没有实现这个方法。结合你的代码场景,问题根源在于:
- 同一个
Draft的保存任务被多次并发触发,多线程环境下draft.localUUID的内存访问出现异常,导致类型识别混乱; DraftQueue仅实现了任务清理逻辑,未跟踪当前正在执行的保存任务,多个任务同时操作同一份draft,引发Swift与ObjC桥接时的类型混淆;Draft的localUUID属性未做线程安全保障,多线程访问时返回了异常的字符串实例。
代码修复方案
1. 完善DraftQueue的任务管理逻辑
修改DraftQueue,添加任务提交方法,确保同一时刻只有一个任务处理同一个draft,同时自动管理任务的生命周期:
actor DraftQueue { private var taskQueue = [String: Task<Void, Never>]() func submitSaveTask(for uuid: String, task: @escaping () async -> Void) { // 取消并移除旧任务 taskQueue[uuid]?.cancel() taskQueue[uuid] = nil // 创建新任务并加入队列,任务完成后自动移除 let newTask = Task { await task() taskQueue[uuid] = nil } taskQueue[uuid] = newTask } func cleanQueueElement(uuid: String) { taskQueue[uuid]?.cancel() taskQueue[uuid] = nil } }
2. 修改saveDraft方法,使用新的任务提交逻辑
private func saveDraft(draft: Draft, mailboxManager: MailboxManager) async { await draftQueue.submitSaveTask(for: draft.localUUID) { await mailboxManager.save(draft: draft) } }
3. 保障Draft属性的线程安全
确保localUUID在多线程环境下返回稳定的字符串实例,比如用@MainActor修饰:
class Draft { @MainActor let localUUID: String // 其他属性定义... }
4. 优化syncDraft的并发控制
避免一次性创建过多并发任务,用TaskGroup限制并发数:
public func syncDraft(mailboxManager: MailboxManager) { Task { let drafts = await mailboxManager.draftWithPendingAction() await withTaskGroup(of: Void.self) { group in let maxConcurrentTasks = 5 // 根据实际情况调整并发数 var activeTaskCount = 0 for draft in drafts { if activeTaskCount >= maxConcurrentTasks { await group.next() activeTaskCount -= 1 } group.addTask { await self.saveDraft(draft: draft, mailboxManager: mailboxManager) } activeTaskCount += 1 } } } }
内容的提问来源于stack exchange,提问作者Ambrdctr
相关产品推荐
相关产品推荐

