迁移Metal ObjC教程至Swift时,UnsafeRawPointer转UnsafeMutableRawPointer报错求解
问题:Swift中UnsafeRawPointer转UnsafeMutableRawPointer的类型转换错误修复
我正在将Apple的《Selecting Device Objects for Compute Processing》Metal Objective-C教程增量迁移至Swift,需对接仍用Objective-C编写的代码,此时遇到报错:
Cannot convert value of type
UnsafeRawPointerto specified typeUnsafeMutableRawPointer
要求不修改传入函数的参数类型,该如何修复?
Swift报错代码片段
func providePositionData(_inout data: NSData) { // Synchronize since positions buffer will be used on another thread objc_sync_enter(self) let rawPtr: UnsafeMutableRawPointer = data.bytes /* error here: Cannot convert value of type 'UnsafeRawPointer' to specified type 'UnsafeMutableRawPointer' */ let positionsBuffer = device.makeBuffer( bytesNoCopy: rawPtr, length: data.count, options: .storageModeManaged, deallocator: nil) positionsBuffer!.label = "Provided Positions" positionsBuffer?.__didModifyRange(NSRange(location: 0, length: data.count)) self.positionsBuffer = positionsBuffer objc_sync_exit(self) }
可正常运行的Objective-C原代码
/// Called to provide positions data to be rendered on the next frame - (void)providePositionData:(NSData *)data { // Synchronize since positions buffer will be used on another thread @synchronized(self) { // Cast from 'const void *' to 'void *' which is okay in this case since updateData was // created with -[NSData initWithBytesNoCopy:length:deallocator:] and underlying memory was // allocated with vm_allocate void *vmAllocatedAddress = (void *)data.bytes; // Create a MTLBuffer with out copying the data id<MTLBuffer> positionsBuffer = [_device newBufferWithBytesNoCopy:vmAllocatedAddress length:data.length options:MTLResourceStorageModeManaged deallocator:nil]; positionsBuffer.label = @"Provided Positions"; [positionsBuffer didModifyRange:NSMakeRange(0, data.length)]; _positionsBuffer = positionsBuffer; } }
传入NSData的Objective-C代码(内存管理逻辑)
// Wrap the memory allocated with vm_allocate with a NSData object which will allow the app to // rely on ObjC ARC (or even MMR) to manage the memory's lifetime. Initialize NSData object // with a deallocation block to free the vm_allocated memory when the object has been // deallocated { // Block to dealloc memory created with vm_allocate void (^deallocProvidedAddress)(void *bytes, NSUInteger length) = ^(void *bytes, NSUInteger length) { vm_deallocate((vm_map_t)mach_task_self(), (vm_address_t)bytes, length); }; NSData *positionData = [[NSData alloc] initWithBytesNoCopy:positionDataAddress length:positionDataSize deallocator:deallocProvidedAddress]; NSData *velocityData = [[NSData alloc] initWithBytesNoCopy:velocityDataAddress length:velocityDataSize deallocator:deallocProvidedAddress]; dataProvider(positionData, velocityData, time); }
修复方案及修正后的代码
核心修复点
Swift中NSData.bytes返回不可变的UnsafeRawPointer,对应Objective-C的const void *。由于已知底层内存是通过vm_allocate分配的可写内存(OC代码注释明确说明),可以通过UnsafeMutableRawPointer(mutating:)安全转换为可变指针,逻辑和OC里的(void *)data.bytes强制转换完全一致。
同时修正两处语法/API调用问题:
- 函数参数的
_inout是输入错误,改为符合Swift语法的_ data: NSData - 将私有API
__didModifyRange替换为公开的didModifyRange方法
修正后的Swift代码
func providePositionData(_ data: NSData) { // Synchronize since positions buffer will be used on another thread objc_sync_enter(self) let rawPtr = UnsafeMutableRawPointer(mutating: data.bytes) let positionsBuffer = device.makeBuffer( bytesNoCopy: rawPtr, length: data.count, options: .storageModeManaged, deallocator: nil) positionsBuffer?.label = "Provided Positions" positionsBuffer?.didModifyRange(NSRange(location: 0, length: data.count)) self.positionsBuffer = positionsBuffer objc_sync_exit(self) }
说明
UnsafeMutableRawPointer(mutating:)是Swift官方提供的转换方式,仅在你明确知道底层内存可写时使用,这里符合OC代码的前置条件,因此是安全的。- 无需修改传入的
NSData参数类型,完全保留原有的OC-Swift对接逻辑。
内容的提问来源于stack exchange,提问作者T.J.
相关产品推荐
相关产品推荐

