Swift3.0 利用泛型设置基类属性的动态类型

2017/05/25 posted in  iOS

在MVVM架构的项目中,我们一般会写一个viewModel的基类和一个controller的基类,在controller的对象中持有viewModel,如下代码

class ViewModel{
    
}
class Controller {

    var viewModel:ViewModel
    
    init(viewModel:ViewModel) {
        self.viewModel = viewModel
    }
}

然后在每一个具体的页面上,都会写一个对应的viewModel和controller分别继承自上面两个基类

class Sub1ViewModel:ViewModel{
    
    let desc = "Sub1"

}
class Sub1Controller:Controller{
    
}

这时如果我想在Sub1Controller中访问Sub1ViewModel中的desc变量,只能在Sub1Controller中这样写

class Sub1Controller:Controller{
    var description{
        let viewModel = self.viewModel as! Sub1ViewModel
        return viewModel.desc
    }
}

由于Swift是强类型语言,不能像OC那样通过类型的强制转换来使基类的属性变成子类的类型,所以你每次使用基类的属性的时候都必须要用as强制转换成子类的类型之后才能访问子类的属性或者方法,当然你可以将这个强制转换包装成一个函数,或者提供另一个变量返回强制转换过的类型。

class Sub1Controller:Controller{
    var sub1ViewModel:Sub1ViewModel{
        return self.viewModel as! Sub1ViewModel
    }
    var description{
    //  let viewModel = self.viewModel as! Sub1ViewModel
        return self.sub1ViewModel.desc  // Or: return self.sub1ViewModel().desc
    }
    
    func sub1ViewModel() -> Sub1ViewModel{
        return self.viewModel as! Sub1ViewModel
    }
} 

但是这两种方法仍然不够优雅,产生了很多冗余的代码,笔者在实践中尝试着使用泛型来是基类的属性类型动态化,是代码看起来更优雅


class ViewModel{
}

class Controller<ModelType:ViewModel> {
    var viewModel:ModelType
    init(viewModel:ModelType) {
        self.viewModel = viewModel
    }
}

class Sub1ViewModel:ViewModel{  
    let desc = "Sub1"
}

class Sub1Controller:Controller<Sub1ViewModel>{
      var description{
        return self.viewModel.desc
    }
}

通过ModelType泛型类型,使得在Sub1Controller中直接通过viewModel就能访问到Sub1ViewModel的属性