Расширения не могут содержать сохраненные свойства, если вы не 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?
}

и получаю две ошибки:

  • 'weak' может применяться только к классам и типам протоколов, привязанным к классу, а не '‹‹тип ошибки>>'
  • Расширения не могут содержать сохраненные свойства

На самом деле я хотел бы предоставить свойство сущности в версиях OSX до 10.13, поэтому дополнительные предложения по этому поводу также приветствуются.


person Bertan Gundogdu    schedule 21.06.2017    source источник
comment
Они действительно не могут. SCNNode, вероятно, является расширением Obj-C, и именно так был создан интерфейс Swift. На самом деле это не действительно Swift.   -  person Sulthan    schedule 21.06.2017
comment
Это может быть вычисляемое свойство, они выглядят так же, как сохраненные свойства в сгенерированном интерфейсе.   -  person Martin R    schedule 21.06.2017


Ответы (4)


В настоящее время это невозможно в Swift. Как отметил Султан, это категория Objective-C, для которой вы видите версию Swift, сгенерированную Xcode.

Теперь Objective-C не поддерживает добавление свойств в категории (расширения называются категориями в Objective-C), но вы можете использовать связанные объекты, чтобы получить то, что вы хотите.

У Мэтта Томпсона есть отличная статья о ассоциированных объектах в его блоге NSHipster: Associated Objects — NSHipster

person Kai Engelhardt    schedule 21.06.2017

Swift 4/iOS 11/Xcode 9.2

Ответы здесь технически правильные, но в любом случае есть решение.

В своем расширении определите приватную структуру с нужными вам полями. Сделайте все статическим, например:

private struct theAnswer {
    static var name: String = ""
}

Я знаю, я знаю, static означает, что это переменная/свойство для всего класса, а не экземпляр. Но на самом деле мы не собираемся ничего хранить в этой структуре.

В приведенном ниже коде для obj_getAssociatedObject и objc_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)
        }
    }
}
person drewster    schedule 26.02.2018

Забавно, у меня такое же чувство, как у ОП. Проверьте это сообщение в блоге самой 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()
    }
}

Вся эта статья посвящена тому, как обрабатывать JSON в Swift, поэтому «Рестораны» не определены ни в одном коде Objective-C.

person AndreG    schedule 07.08.2017

Вы можете попробовать этот код, я использую Xcode 8.3.3 и Swift 3.1:

import SceneKit
import GameplayKit

extension SCNNode {

    @available(OSX 10.13, *)
    public var entity: GKEntity? {
        return self.entity
    }
}
person mustafa elsebaie    schedule 15.09.2017
comment
получить разрешены только свойства - person Ryan Francesconi; 21.05.2021