我试图继承MKPolyline。但是问题是,MKPolyline没有任何指定的初始化程序。这是我尝试做的事情:
import MapKit
class MyPolyline: MKPolyline {
let locations: [Location]
init(locations: [Location]) {
self.locations = locations
var coordinates: [CLLocationCoordinate2D] = []
for location in locations {
coordinates.append(location.coordinate)
}
super.init(coordinates: coordinates, count: coordinates.count)
}
}
Run Code Online (Sandbox Code Playgroud)
但是我Must call a designated initializer of the superclass 'MKPolyline'在super.init通话中遇到了错误。据我所知,MKPolyline没有任何指定的初始化程序。
我该怎么办?如何子类化没有指定初始化程序的类?
正如您所知,指定的初始值设定项提供了一种从一组参数创建对象的方法。每个 Swift 对象必须至少有一个指定的初始值设定项,尽管在某些情况下 Swift 可能会默认提供它们。
\n\n对于类,如果该类的所有存储属性在其声明中提供默认值,则将提供默认初始值设定项。否则,类创建者必须提供指定的初始值设定项。然而,即使显式提供了指定的 init,也不需要具有可供您访问的访问控制级别。
\n\n所以MKPolyline肯定至少有一个指定的 init,但它可能对你不可见。这实际上使得子类化变得不可能,但是还有其他选择。
我立即想到了两种选择:
\n\nSwift 的一大特点是,即使原始类是在另一个模块(甚至是第三方模块)中定义的,也可以扩展类。
\n\n您遇到的问题是MKPolyline定义了便利 init,但没有定义公共指定的 init。这是一个问题,因为 Swift 有一条规则,指定的 init 必须调用其直接超类的指定的 init。由于这条规则,即使是指定的 init 也无法工作,即使在扩展中也是如此。幸运的是,Swift 有方便的初始化器。
便利 init 只是初始化器,通过最终调用同一类中指定的 init 来工作。他们不会向上或向下授权,而是横向授权,可以这么说。与指定 init 不同,便利 init 可以调用指定 init 或另一个便利 init,只要它们位于同一类中即可。
\n\n利用这些知识,我们可以创建一个扩展来MKPolyline声明一个便利 init,它将调用其他便利 init 之一。我们可以这样做,因为在扩展内部就像在原始类本身中一样,因此这满足了便利 inits 的同类要求。
基本上,您只需拥有一个带有便利 init 的扩展,该扩展将采用 的数组Location,将它们转换为坐标,并将它们传递给已经定义的便利 init MKPolyline。
如果您仍然想将位置数组作为存储属性保存,我们会遇到另一个问题,因为扩展可能不会声明存储属性。我们可以通过创建locations一个计算属性来解决这个问题,该属性只是从现有的getCoordinates方法中读取。
这是代码:
\n\nextension MKPolyline {\n\n var locations: [Location] {\n guard pointCount > 0 else { return [] }\n\n let defaultCoordinate = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)\n var coordinates = [CLLocationCoordinate2D](repeating: defaultCoordinate, count: pointCount)\n getCoordinates(&coordinates, range: NSRange(location: 0, length: pointCount))\n\n // Assuming Location has an init that takes in a coordinate:\n return coordinates.map({ Location(coordinate: $0) })\n }\n\n convenience init(locations: [Location]) {\n\n let coordinates = locations.map({ $0.coordinate })\n\n self.init(coordinates: coordinates, count: coordinates.count)\n }\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这就是发生的事情。在底部,我们有一个便利 init ,与您已经做的非常相似,只是它调用了便利 init ,self因为我们不在子类中。我还使用了map一种更简单的方法将坐标从Location.
最后,我们有一个在幕后locations使用该方法的计算属性。getCoordinates我提供的实现可能看起来很奇怪,但这是必要的,因为该getCoordinates函数是基于 Objective-C\xe2\x80\x93 的,并且UnsafeMutablePointer在导入到 Swift 时使用。因此,您需要首先声明一个CLLocationCoordinate2D具有精确长度的可变数组 ,然后将其传递给getCoordinates,它将在参数指定的范围内填充传递的数组range。参数之前&的coordinates参数告诉 Swift 它是一个 inout 参数,并且可能会被函数改变。
但是,如果您需要locations成为存储属性才能容纳更复杂的Location对象,则可能需要使用下面描述的第二个选项,因为扩展可能没有存储属性。
该解决方案感觉不像前一个解决方案那样“迅速”,但它是我所知道的唯一一个可以让您拥有存储属性的解决方案。基本上,您只需定义一个包含底层MKPolyline实例的新类:
class MyPolyline {\n\n let underlyingPolyline: MKPolyline\n\n let locations: [Location]\n\n init(locations: [Location]) {\n self.locations = locations\n\n let coordinates = locations.map { $0.coordinate }\n self.underlyingPolyline = MKPolyline(coordinates: coordinates, count: coordinates.count)\n }\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这种方法的缺点是,只要您想用作MyPolyline,MKPolyline您就需要使用myPolyline.underlyingPolyline来检索实例。据我所知,解决这个问题的唯一方法是使用此问题的已接受答案所描述的方法,以便将您的类型桥接到MKPolyline,但这使用了未记录的协议,因此可能不会被 Apple 接受。
| 归档时间: |
|
| 查看次数: |
632 次 |
| 最近记录: |