“扩展可能不包含存储的属性”,除非你是Apple?我错过了什么?

问题描述:

为什么苹果能做到这一点:“扩展可能不包含存储的属性”,除非你是Apple?我错过了什么?

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? 
} 

,并得到了两个错误:

  • '弱' 可能只适用到类和类绑定的协议类型,而不是'< <错误类型>>'
  • 扩展可能不包含存储的原型rtyies

我想实际做的是在10.13之前提供一个OSX版本的实体属性,所以额外的建议也是值得欢迎的。

+2

他们真的不能。 'SCNNode'可能是一个Obj-C扩展,这就是Swift接口生成的方式。这真是无效的Swift。 – Sulthan

+4

它可以是一个计算属性,它们看起来与生成的接口中存储的属性相同。 –

这在Swift中是不可能的。正如Sulthan所指出的,这是一个Objective-C类别,您可以看到由Xcode生成的Swift版本。现在,Objective-C不轻易支持在类别中添加属性(扩展在Objective-C中称为类别),但是您可以使用关联对象来获得所需内容。

马特·汤普森有关于他的NSHipster博客关联的对象一个伟大的文章:Associated Objects - NSHipster

滑稽我有同样的感受为OP。由苹果自己检查这个博客帖子: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代码中定义。

你可以试试这个代码,我使用的Xcode 8.3.3和3.1雨燕:

import SceneKit 
import GameplayKit 

extension SCNNode { 

    @available(OSX 10.13, *) 
    public var entity: GKEntity? { 
     return self.entity 
    } 
} 

斯威夫特4/iOS的11/9.2的Xcode

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

在你的扩展中,用你想要的字段定义一个私有结构。使一切都静态,像这样:

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) 
     } 
    } 
}