2016-07-04 14 views
1

このコードはコンパイルされず、まるで愚かに聞こえるかもしれませんが、なぜそれが重要なのかを説明します!@objcプロトコルとオプションと拡張機能を同時に使用するには?

@objc protocol p { 
    optional func f1() 
    func f2() 
} 

extension p { 
    func f1() { } 
    func f2() { } 
} 


class foo: p { 
} 

コンパイラは、Type c does not conform to protocol 'p'を言うと、あなたは同時に、オプションおよび拡張を@objc使用することはできません(どちらかこのシナリオでローミングサービスをしない)ので、それはおそらくです。しかし、次の例を考えてみます。

私は私の延長にあるプロトコルで定義された非任意の方法(私は@objcを使用した主な理由)にセレクタを設定したい:

func f1() { } - >func f1() { ... #selector(Self.f2) ... }

そして、私はまた、私のf2()関数がデフォルトの振る舞いを持つことを望みます。私がf2()optionalとマークした場合、#selectorで使用することはできません。なぜなら、コンパイラは、このメソッドが実際に必要な場合に存在するかどうかわからないからです。確かに、グローバルメソッドのような厄介な回避策がたくさんありますが、Selectorをメソッドとして入力するなどしていますが、それを達成するためのきれいな方法はありますか?


のViewControllerがRefreshableContentLoaderを実装している場合これは、デフォルトrefresh機能を見つけることができません、今すぐ実践的な問題

@objc 
protocol Refreshable { 
    weak var refreshControl: UIRefreshControl? { get set } 
    optional func setupRefreshControl() 
    func refresh() 
} 

@objc 
protocol ContentLoader { 
    func load(reset: Bool) 
} 

extension Refreshable where Self: ContentLoader { 
    func refresh() { 
     delay(0.75) { [weak self] in 
      self?.load(true) 
     } 
    } 
} 

extension Refreshable where Self: UICollectionViewController { 
    func setupRefreshControl() { 
     let newRefreshControl = UIRefreshControl() 

     newRefreshControl.tintColor = UIColor.grayColor() 
     newRefreshControl.addTarget(self, action: #selector(Self.refresh), forControlEvents: .ValueChanged) 
     collectionView?.addSubview(newRefreshControl) 
     refreshControl = newRefreshControl 
    } 
} 

ですが、それはsetupRefreshControlを見つけるん。だから私はrefreshもオプションとしてマークしてみましたが、それを行うことでセレクタにそれ以上送ることはできません。 >optional func refresh()

let str = "refresh" 
let sel = Selector(str) 

それはイエスのコンパイラをsilentsが、私はこれがあると思いunrecognized selector sent to instance....

+0

エラーを再現できません。実際のコード( 'f1'と' f2'とプロトコル 'p'に準拠させたいクラスの関連コード)を他の人と私があなたを助けるために投稿してください。また、あなたが達成しようとしていることを明示してください。非常に散在しており、不明確です。 – Ike10

+0

Objcではオプションが問題になります。おそらくあなたができることは、それぞれが特定の目的のために複数のデリゲートを設定することです。デリゲートが設定されていない場合、関連付けられた関数は呼び出されません。 – Feldur

+0

@ Ike10質問を更新しました:) – farzadshbfn

答えて

1

を...どちらかの作業に上昇しない -

func refresh()は:

は、私もこれを試してみました迅速に(それが@objcプロトコルに橋渡しするために)不可能である。しかしこれは(Obj-cに関連するオブジェクトを使って)unrecognized selector sent to instance...問題を解決するための回避策です。

fileprivate class AssociatedObject: NSObject { 
    var closure: (() ->())? = nil 

    func trigger() { 
     closure?() 
    } 
} 

// Keys should be global variables, do not use, static variables inside classes or structs. 
private var associatedObjectKey = "storedObject" 

protocol CustomProtocol: class { 
    func setup() 
} 

extension CustomProtocol where Self: NSObject { 

    fileprivate var associatedObject: AssociatedObject? { 
     get { 
      return objc_getAssociatedObject(self, &associatedObjectKey) as? AssociatedObject 
     } 

     set { 
      objc_setAssociatedObject(self, &associatedObjectKey, newValue, .OBJC_ASSOCIATION_RETAIN) 
     } 
    } 


    func setup() { 
     let object = AssociatedObject() 
     object.closure = { [weak self] in // Do not forget to use weak in order to avoid retain-cycle 
      self?.functionToCallIndirectlyWithSelector() 
     } 

     let selector = #selector(object.trigger) 
//  Uncomment next line to test it's functionality 
     object.perform(selector) 

//  Here, you must add selector to the target which needs to call the selector, for example: 
//  refreshControl.addTarget(object, action: selector, forControlEvents: .valueChanged) 

     self.associatedObject = object 
    } 

    func functionToCallIndirectlyWithSelector() { 
     print("Function got called indirectly.") 
    } 
} 


class CustomClass: NSObject, CustomProtocol {} 

let instance = CustomClass() 

instance.setup() 

私はそれが運動場での機能のテストすることができるようにSelf: NSObject制約を追加し、私はそれが必要だかどうかはわかりません。

+0

あなたの 'associatedObjectKey'は安全ではありません。スウィフトは静的な参照を取るときにポインタの安定性を保証しません。唯一の安全な方法は、タイプレベルの静的ではなくトップレベルのグローバル変数を使用することです。 –

+0

@KevinBallardこれに関する書類はありますか?私は今、答えを変えるつもりですが、なぜそのようなことが起こるのかを知りたいのです。 – farzadshbfn

+1

'&associatedObjectKey'で静的なvarへの参照をしています。関連付けられたオブジェクトはこれの値を見ることはありません。厳密にはポインタを使用するため、渡すポインタは常に同じでなければなりません。しかし、Swiftは '&associatedObjectKey'が常に同じポインタになるとは保証しません。コンパイラがこれを保証する唯一の場所は、(たとえ言語が決して行っていなくても)トップレベルのプロパティです。この情報は、コンパイラーエンジニアから直接得られます。 –

関連する問題