将GPUImage过滤器子类移植到Swift

时间:2014-10-22 14:48:36

标签: ios objective-c xcode swift gpuimage

我正在尝试将应用程序从Objective-C移植到Swift,但我遇到了GPUImageFilter子类的问题。

在Obj-C中,继承GPUImageFilter并使用不同的片段着色器就像

一样简单
- (id)init;
{
    NSString *fragmentShaderPathname = [[NSBundle mainBundle] pathForResource:@"TestShader" ofType:@"fsh"];
    NSString *fragmentShaderString = [NSString stringWithContentsOfFile:fragmentShaderPathname encoding:NSUTF8StringEncoding error:nil];

    if (!(self = [super initWithFragmentShaderFromString:fragmentShaderString]))
    {
        return nil;
    }

    return self;
}

我无法弄清楚如何在Swift中执行此操作。这是我的新初始化程序:

override init() {
    let fragmentShaderPathname = NSBundle.mainBundle().pathForResource("TestShader", ofType: "fsh")!
    let fragmentShaderString = NSString(contentsOfFile: fragmentShaderPathname, encoding: NSUTF8StringEncoding, error: nil)

    super.init(fragmentShaderFromString: fragmentShaderString)
}

一旦调用此应用程序,应用程序就会崩溃并显示以下错误消息: /Users/peterwunder/Documents/XCode/welp/welp/GPUImageTestFilter.swift: 12: 7: fatal error: use of unimplemented initializer 'init(vertexShaderFromString:fragmentShaderFromString:)' for class 'welp.GPUImageTestFilter'

我做错了什么?

1 个答案:

答案 0 :(得分:7)

有趣的问题!由于Swift和Objective-C之间的初始化不同,您将被绊倒。如果GPUImageFilter类是用Swift编写的,那么编译器就不会让你进行这种设置,但是因为你正在混合使用它。匹配它直到运行时才会抱怨。以下是您尝试实例化子类时发生的情况:

  1. 您致电let myFilter = CustomGPUImageFilter()
  2. 您的init()加载着色器字符串并调用super.init(fragmentShaderString: "foo")
  3. GPUImageFilter.init(fragmentShaderString:)来电[self initWithVertexShaderFromString:@"bar" fragmentShaderFromString:@"foo"]
  4. 最后一行中self的类型是什么?如果您从子类的初始化程序调用,self的类型实际上是您的子类的类型:CustomGPUImageFilter。因此被调用的初始化程序实际上在您的子类上。为什么这是一个问题?

    不幸的是,如果Swift类定义了自己的指定的初始值设定项,那么它们就不会自动从其超类继承初始化器,这就是你的init()。因此运行时尝试调用CustomGPUImageFilter.init(vertexShaderFromString:fragmentShaderFromString:),找不到它,并且崩溃并显示该错误消息。

    令人高兴的是,修复非常简单。如果您将初始化程序标记为convenience init,则不再定义指定的初始值设定项,这意味着您的子类继承GPUImageFilter的初始值设定项。然后在上面的步骤3中,运行时将在子类上找到继承的初始化程序并执行而不会出现问题。请注意,您需要拨打self.init...而不是super.init...

    convenience override init() {
        let fragmentShaderPathname = NSBundle.mainBundle().pathForResource("TestShader", ofType: "fsh")!
        let fragmentShaderString = NSString(contentsOfFile: fragmentShaderPathname, encoding: NSUTF8StringEncoding, error: nil)
    
        self.init(fragmentShaderFromString: fragmentShaderString)
    }
    

    有关此过程的更多信息,请阅读Swift文档中有关class inheritance and initialization的部分。该过程显着与Objective-C不同。