ios - 与 MIDIPacketNext 一起崩溃
问题描述
十年前,我使用 PGMidi 用 Objective-C 编写了一个应用程序,它是 Core MIDI 的辅助类。PGMidi 的输入函数如下所示:
static void PGMIDIReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) {
PGMidiSource *source = arc_cast<PGMidiSource>(srcConnRefCon);
[source midiRead:pktlist fromPort:source.name];
}
- (void) midiRead:(const MIDIPacketList *)pktlist fromPort:(NSString *)port {
NSArray *delegates = self.delegates;
for (NSValue *delegatePtr in delegates) {
id<PGMidiSourceDelegate> delegate = (id<PGMidiSourceDelegate>)[delegatePtr pointerValue];
[delegate midiSource:self midiReceived:pktlist fromPort:port];
}
}
这会将数据传递给我的应用程序功能:
static NSMutableArray *packetToArray(const MIDIPacket *packet) {
NSMutableArray *values = [[NSMutableArray alloc] initWithCapacity:3];
NSNumber *value;
for (int j=0; j<packet->length; j++) {
value = [[NSNumber alloc] initWithUnsignedInt:packet->data[j]];
[values addObject:value];
}
return values;
}
- (void)midiSource:(PGMidiSource *)midi midiReceived:(const MIDIPacketList *)packetList fromPort:(NSString *)port {
const MIDIPacket *packet = &packetList->packet[0];
for (int i=0; i<packetList->numPackets; ++i) {
NSDictionary *data = [[NSDictionary alloc] initWithObjectsAndKeys:
packetToArray(packet), @"values",
port, @"port",
nil];
[self performSelectorOnMainThread:@selector(receiveMidiData:) withObject:data waitUntilDone:FALSE];
packet = MIDIPacketNext(packet);
}
}
这一直运作良好。但今年早些时候,我将我的应用程序转换为 Swift。我单独离开了 PGMidi 类,但将我的应用程序函数重写为:
func packetToArray(_ packet: MIDIPacket) -> [Int] {
var values = [Int]()
let packetMirror = Mirror(reflecting: packet.data)
let packetValues = packetMirror.children.map({ $0.value as? UInt8 })
for i in 0..<Int(packet.length) {
if (packetValues.count > i), let value = packetValues[i] {
values.append(Int(value))
}
}
return values
}
@objc func midiSource(_ midi: PGMidiSource, midiReceived packetList: UnsafePointer<MIDIPacketList>, fromPort port: String) {
var packet = packetList.pointee.packet // this gets the first packet
for _ in 0..<packetList.pointee.numPackets {
let packetArray = App.packetToArray(packet)
let data: [AnyHashable : Any] = [
"values" : packetArray,
"port" : port
]
self.performSelector(onMainThread: #selector(receiveMidiData(_:)), with: data, waitUntilDone: false)
packet = MIDIPacketNext(&packet).pointee
}
}
这主要是可行的,但我遇到了一些崩溃MIDIPacketNext
。这并不一致,并且似乎在大量数据进入时发生,例如来自键盘上的表达式轮。一份崩溃日志包括以下内容:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x000000016b78914c
VM Region Info: 0x16b78914c is not in any region. Bytes after previous region: 37197 Bytes before following region: 360050356
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
Stack 16b6f8000-16b780000 [ 544K] rw-/rwx SM=PRV thread 6
---> GAP OF 0x15768000 BYTES
unused shlib __TEXT 180ee8000-180f2c000 [ 272K] r-x/r-x SM=COW ... this process
另一个略有不同,但指向同一行代码:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Subtype: KERN_PROTECTION_FAILURE at 0x000000016beb46cc
VM Region Info: 0x16beb46cc is in 0x16beb4000-0x16beb8000; bytes after start: 1740 bytes before end: 14643
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
Stack 16be2c000-16beb4000 [ 544K] rw-/rwx SM=PRV thread 5
---> STACK GUARD 16beb4000-16beb8000 [ 16K] ---/rwx SM=NUL ... for thread 9
Stack 16beb8000-16bf40000 [ 544K] rw-/rwx SM=PRV thread 9
我是否应该以不同的方式访问数据包以避免这种情况,或者我可以做些什么来检测错误情况并避免崩溃?
解决方案
Eugene Dudnyk的评论让我找到了解决方案。我重写了我的 midiSource 委托函数,如下所示,添加了 UnsafePointer 扩展:
@objc func midiSource(_ midi: PGMidiSource, midiReceived packetList: UnsafePointer<MIDIPacketList>, fromPort port: String) {
let packetList = packetList.loadUnaligned(as: MIDIPacket.self, count: Int(packetList.pointee.numPackets))
for packet in packetList {
let packetArray = App.packetToArray(packet)
let data: [AnyHashable : Any] = [
"values" : packetArray,
"port" : port
]
self.performSelector(onMainThread: #selector(receiveMidiData(_:)), with: data, waitUntilDone: false)
}
}
extension UnsafePointer {
func loadUnaligned<T>(as: T.Type, count: Int) -> [T] {
assert(_isPOD(T.self)) // relies on the type being POD (no refcounting or other management)
let buffer = UnsafeMutablePointer<T>.allocate(capacity: count)
defer { buffer.deallocate() }
memcpy(buffer, self, MemoryLayout<T>.size * count)
return (0..<count).map({ index in buffer.advanced(by: index).pointee })
}
}
没有更多的崩溃!
推荐阅读
- odata - Odata 共享点休息 api 过滤器
- javascript - 如果用户已经存在于数据库中,我想通过 php get 方法发送错误消息,然后在没有 GET 变量的情况下重置 url
- python - 这些方法在models.py中基本上是做什么的
- ios - 错误:在此上下文中,“事务”对于类型查找不明确
- python - 如何在 Python 中将表达式字符串拆分为列表?
- python - Loc Pandas DataFrames 上的日期范围
- sql - 编写存储过程的推荐方法是什么?
- mysql - SQL - 显示帖子(5 小时前)
- android - 如何使用 Spannablestring 更改文本颜色和字体
- bash - 修复 shell 脚本以增加 semversion