"Extensions may not contain stored properties"除非你是苹果?我错过了什么?



苹果怎么能做到这一点:

import CoreGraphics
import GameplayKit
import simd
/**
 @header

 SceneKit framework category additions related to GameplayKit integration.

 @copyright 2017 Apple, Inc. All rights reserve.
 */
extension SCNNode {

    /**
     * The GKEntity associated with the node via a GKSCNNodeComponent.
     *
     * @see GKEntity
     */
    @available(OSX 10.13, *)
    weak open var entity: GKEntity?
}
/**
 * Adds conformance to GKSceneRootNodeType for usage as rootNode of GKScene 
 */
extension SCNScene : GKSceneRootNodeType {
}

。我不能这样做:

extension SCNNode {
    weak open var ntity: GKEntity?
}

并得到两个错误:

    ">
  • 弱"只能应用于类和类绑定协议类型,而不适用于"<<错误类型>>
  • 扩展可能不包含存储的属性

我实际上想做的是在 10.13 之前的 OSX 版本上提供一个实体属性,因此也欢迎其他建议。

Swift 4/iOS 11/Xcode 9.2

这里的答案在技术上是正确的,但无论如何这是一个解决方案。

在扩展中,使用所需的字段定义私有结构。 使所有内容都静态,如下所示:

private struct theAnswer {
    static var name: String = ""
}
我知道,

我知道,静态意味着它是一个类范围的变量/属性,而不是一个实例变量/属性。 但是我们实际上不会在这个结构中存储任何东西。

在下面的代码中,obj_getAssociatedObjectobjc_setAssociatedObject都需要一个 UnsafeRawPointer 作为键。 我们使用这些函数来存储与此唯一实例关联的键/值对。

下面是带有字符串的完整示例:

import Foundation
class ExtensionPropertyExample {
    // whatever
}

extension ExtensionPropertyExample {
    private struct theAnswer {
        static var name: String = ""
    }
    var name: String {
        get {
            guard let theName = objc_getAssociatedObject(self, &theAnswer.name) as? String else {
                return ""
            }
            return theName
        }
        set {
            objc_setAssociatedObject(self, &theAnswer.name, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }
}

这在 Swift 中目前是不可能的。正如 Sulthan 所指出的,这是一个 Objective-C 类别,你可以看到由 Xcode 生成的 Swift 版本。

现在,Objective-C

不容易支持在类别中添加属性(扩展在Objective-C中称为类别(,但你可以使用关联的对象来获取你想要的东西。

Mattt Thompson在他的NSHipster博客上有一篇关于关联对象的好文章:Associated Objects - NSHipster

有趣的是,我和OP有同样的感觉。 查看Apple本身的这篇博客文章: https://developer.apple.com/swift/blog/?id=37

您会注意到它们显然违反了唯一的规则,并且它们解释了如何将 JSON 与不可编译的代码一起使用。

extension Restaurant {
    private let urlComponents: URLComponents // base URL components of the web service
    private let session: URLSession // shared session for interacting with the web service
    static func restaurants(matching query: String, completion: ([Restaurant]) -> Void) {
        var searchURLComponents = urlComponents
        searchURLComponents.path = "/search"
        searchURLComponents.queryItems = [URLQueryItem(name: "q", value: query)]
        let searchURL = searchURLComponents.url!
        session.dataTask(url: searchURL, completion: { (_, _, data, _)
            var restaurants: [Restaurant] = []
            if let data = data,
                let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                for case let result in json["results"] {
                    if let restaurant = Restaurant(json: result) {
                        restaurants.append(restaurant)
                    }
                }
            }
            completion(restaurants)
        }).resume()
    }
}

整篇文章都是关于如何在 Swift 中处理 JSON,所以"餐厅"没有在任何 Objective-C 代码中定义。

在 Swift 5, Xcode 13 中,我能够使用 static 将属性直接添加到扩展中(无需创建内部结构等(。

例如:

extension String {
   static var onboarded = "onboarded"
   static var locationAllowed = "locationAllowed"
}

用法:

@AppStorage(.locationAllowed) var locationAllowed = false
@AppStorage(.onboarded) var a = false

@AppStorage需要一个"字符串"作为参数,所以我只使用了扩展中的.onboarded

你可以试试这段代码,我使用的是Xcode 8.3.3和Swift 3.1:

import SceneKit
import GameplayKit
extension SCNNode {
    @available(OSX 10.13, *)
    public var entity: GKEntity? {
        return self.entity
    }
}

相关内容

最新更新