我是目标 C 的新手,并且对来自 Apple 的这个示例项目 展示一些 Metal 特性的代码有疑问。
代码包含位于函数范围内的几个块。在每个块内部,分配一些资源,并将分配的资源传递给各种方法。
在函数内的块内这样做的原因是什么?
另外,我可以看到在每个块内声明的变量正在块范围之外消失,但是如何为这些变量指向的资源管理内存?它是由自动释放池管理的吗?
- (void)loadMetal
{
NSError* error = nil;
NSLog(@"Selected Device: %@", _mtlDevice.name);
_mtkView.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
_mtkView.depthStencilPixelFormat = MTLPixelFormatDepth32Float;
_commandQueue = [_mtlDevice newCommandQueue];
id<MTLLibrary> defaultLibrary = [_mtlDevice newDefaultLibrary];
// Create the render pipeline to shade the geometry.
{
id<MTLFunction> vertexFunc = [defaultLibrary newFunctionWithName:@"vertexShader"];
id<MTLFunction> fragmentFunc = [defaultLibrary newFunctionWithName:@"fragmentShader"];
MTLRenderPipelineDescriptor* renderPipelineDesc = [MTLRenderPipelineDescriptor new];
renderPipelineDesc.vertexFunction = vertexFunc;
renderPipelineDesc.fragmentFunction = fragmentFunc;
renderPipelineDesc.vertexDescriptor = nil;
renderPipelineDesc.colorAttachments[0].pixelFormat = _mtkView.colorPixelFormat;
renderPipelineDesc.depthAttachmentPixelFormat = _mtkView.depthStencilPixelFormat;
// means "i don't need a depth buffer"
renderPipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
_renderPSO = [_mtlDevice newRenderPipelineStateWithDescriptor:renderPipelineDesc error:&error];
NSAssert(_renderPSO, @"Failed to create forward plane with sparse texture render pipeline state");
}
// Create the default depth stencil state for the depth test.
{
MTLDepthStencilDescriptor* desc = [MTLDepthStencilDescriptor new];
desc.depthWriteEnabled = YES;
desc.depthCompareFunction = MTLCompareFunctionLess;
_depthStencilState = [_mtlDevice newDepthStencilStateWithDescriptor:desc];
}
// Prefill the render pass descriptors with the clear, load, and store actions.
{
_renderPassDescriptor = [MTLRenderPassDescriptor new];
_renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.1, 0.1, 0.1, 1.0);
_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
_renderPassDescriptor.depthAttachment.clearDepth = 1.0;
_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
}
}
在函数内的块内这样做的原因是什么?
首先你应该区分 Objective-C 块,它们是 Objective-C 中仿函数的语法糖(可以执行某些任务的一流对象),具有以下语法:
^{
NSLog(@"Objective-C block");
}
和匿名块,继承自C并引入作用域:
{
printf("Anonymous block");
}
在给定的代码中,它是一个匿名块,它的主要好处是它有助于稍微解耦代码,即在块内引入的变量在块外是不可见的。如果您启用了 ARC,另一个副作用是编译器确保在作用域末尾释放所有本地非保留对象(但不一定释放)。
这对于最后一段代码来说似乎有些多余,省略匿名块不会改变函数在这里的工作方式:
// Prefill the render pass descriptors with the clear, load, and store actions.
{
_renderPassDescriptor = [MTLRenderPassDescriptor new];
_renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.1, 0.1, 0.1, 1.0);
_renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
_renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
_renderPassDescriptor.depthAttachment.clearDepth = 1.0;
_renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear;
_renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
}
..但它仍然为读者提供了代码不同部分的清晰视觉分离。
另外,我可以看到在每个块内声明的变量都在块范围之外消失
如果您指的是这些部分:
...
// Create the render pipeline to shade the geometry.
{
...
_renderPSO = [_mtlDevice newRenderPipelineStateWithDescriptor:renderPipelineDesc error:&error];
NSAssert(_renderPSO, @"Failed to create forward plane with sparse texture render pipeline state");
}
// Create the default depth stencil state for the depth test.
{
...
_depthStencilState = [_mtlDevice newDepthStencilStateWithDescriptor:desc];
}
...
..那么传递参数的函数实际上可以保留对象,但在 Objective-C 中这里没有争议:most 语言中的对象是动态分配的,因此它们可以超过声明的范围in. 通常 ARC 会确保所有引用对象的变量都是有效的,只要它们是可寻址的:
- (void)foo {
NSObject *obj;
{
NSObject *localObj = [NSObject new];
obj = localObj;
}
// The `obj` have the `localObj` retained here, so it keeps on living
NSLog(@"%@", obj);
// `obj` is released at the of the scope it was declared in;
}
在幕后,ARC 的作用可以解读如下:
- (void)foo {
NSObject *obj;
{
NSObject *localObj = [NSObject new];
obj = [localObj retain];
[localObj release];
}
// The `obj` have the `localObj` retained here, so it keeps on living
NSLog(@"%@", obj);
// `obj` is released at the of the scope it was declared in;
[obj release];
}