我对你的代码有点困惑。为什么被getCoordinateFrom
标记为mutating
?也许你打算写这样的东西。
mutating func getCoordinatesFromAddress() {
CLGeocoder().geocodeAddressString(address) { placemarks, error in
guard let coordinate = placemarks?.first?.location?.coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
现在这个函数正在变异(它修改self
),并且
wildernessLodge.getCoordinateFrom { coordinate, error in ... }
可以替换为
wildernessLodge.getCoordinatesFromAddress()
离开该getCoordinateFrom
方法的唯一原因是,如果在代码中的某个位置,您打算从地址获取坐标但不更新结构中的坐标。我无法想象这样做的充分理由,所以我建议用其他方法替换该getCoordinateFrom
方法。
或者,如果您通常打算在创建此类型的值后立即设置坐标,您可能需要考虑这样的事情。
init(name: String, address: String) {
self.name = name
self.address = address
CLGeocoder().geocodeAddressString(address) { placemarks, error in
guard let coordinate = placemarks?.first?.location?.coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
或者
init(name: String, address: String) {
self.name = name
self.address = address
self.getCoordinatesFromAddress()
}
然后,您可以创建一个organization
usingorganization(name: name, address: address)
并自动正确设置坐标。
如果这些都不令人满意,也许您应该创建两个不同的结构来捕获您想要的行为。
struct Organization {
var name: String
var address: String
func withCoordinates(completion: @escaping(_ coordinate: CLLocationCoordinate2D?, _ error: Error?) -> () ) {
CLGeocoder().geocodeAddressString(address) { placemarks, error in
completion(placemarks?.first?.location?.coordinate, error)
}
}
}
struct OrganizationWithCoordinates {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
init(from organization: Organization) {
self.name = organization.name
self.address = organization.address
organization.withCoordinates { coordinate, error in
guard let coordinate = coordinate, error == nil else { return }
self.latitude = coordinate.latitude
self.longitude = coordinate.longitude
}
}
}
我更喜欢这样的方法,但我喜欢有很多类型。
最后,如评论中所述,如果您真的只关心简洁,您可以替换
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
和
var coordinates: CLLocationCoordinate2D
var latitude: CLLocationDegrees { coordinates.latitude }
var longitude: CLLocationDegrees { coordinates.longitude }
然后替换
wildernessLodge.latitude = coordinate.latitude
wildernessLodge.longitude = coordinate.longitude
和
wildernessLodge.coordinates = coordinate
事实上,您应该可以随意组合这些方法中的任何一种。
编辑:正如所指出的,这些解决方案不能按原样工作。基本的张力是尝试CLGeocoder
同步使用 's async 方法。一种解决方案是使用类而不是结构。另一种方法是使用上述withCoordinate
方法的修改:
struct Organization {
var name: String
var address: String
func withCoordinate(callback: @escaping (OrganizationWithCoordinate?, Error?) -> Void) {
CLGeocoder().geocodeAddressString(self.address) { placemarks, error in
if let coordinate = placemarks?.first?.location?.coordinate, error == nil {
let orgWithCoord = OrganizationWithCoordinate(name: self.name, address: self.address, latitude: coordinate.latitude, longitude: coordinate.latitude)
callback(orgWithCoord, nil)
} else {
callback(nil, error)
}
}
}
}
struct OrganizationWithCoordinate {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
}
Organization(name: "Disney's Wilderness Lodge", address: "901 Timberline Dr, Orlando, FL 32830").withCoordinate { orgWithCoord, error in
guard let orgWithCoord = orgWithCoord, error == nil else {
print("Error")
return
}
print(orgWithCoord)
}
这包含了CLGeocoder
.
另一种解决方案可能是使用如下强制CLGeocoder
同步。DispatchSemaphore
我认为这些在 Playgrounds 中不能正常工作,但这应该在实际应用程序中工作。
struct Organization {
var name: String
var address: String
var latitude: CLLocationDegrees
var longitude: CLLocationDegrees
init(name: String, address: String) throws {
self.name = name
self.address = address
var tempCoordinate: CLLocationCoordinate2D?
var tempError: Error?
let sema = DispatchSemaphore(value: 0)
CLGeocoder().geocodeAddressString(address) { placemarks, error in
tempCoordinate = placemarks?.first?.location?.coordinate
tempError = error
sema.signal()
}
// Warning: Will lock if called on DispatchQueue.main
sema.wait()
if let error = tempError {
throw error
}
guard let coordinate = tempCoordinate else {
throw NSError(domain: "Replace me", code: -1, userInfo: nil)
}
self.longitude = coordinate.longitude
self.latitude = coordinate.latitude
}
}
// Somewhere in your app
let queue = DispatchQueue(label: "Some queue")
queue.async {
let wildernessLodge = try! Organization(name: "Disney's Wilderness Lodge", address: "901 Timberline Dr, Orlando, FL 32830")
DispatchQueue.main.async {
print(wildernessLodge)
}
}
在这里,创建了一个新的队列来做Organization
相关的工作,以避免锁定主队列。在我看来,这种方法创建的代码看起来最不笨拙,但可能不是性能最高的选择。位置 API 是异步的是有原因的。