2016-07-04 6 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 사용할 수 없습니다 (그리고 중이 시나리오의 sence가되지 않습니다) 때문 어쩌면입니다. (주된 이유는 내가 @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 프로토콜에 연결되는 방식 때문에) 신속하게는 불가능합니다. 그러나이 문제는 unrecognized selector sent to instance... 문제를 해결하기 위해 Obj-c와 관련된 객체를 사용하여 해결됩니다. 것이 필요 나하지 않을 경우

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'는 안전하지 않습니다. Swift는 정적에 대한 참조를 가져올 때 포인터 안정성을 보장하지 않습니다. 유일하게 안전한 것은 유형 레벨 정적 대신 최상위 레벨 전역 변수를 사용하는 것입니다. –

+0

@KevinBallard 이에 대한 문서가 있습니까? 당장은 대답을 바꾸 겠지만 그런 일이 정확히 일어날 이유를 알고 싶습니다. – farzadshbfn

+1

'& associatedObjectKey'를 사용하여 정적 var에 대한 참조를 취하고 있습니다. 연결된 객체는이 값을 보지 않으며 엄격하게 포인터를 사용하므로 전달하는 포인터는 항상 동일해야합니다. 그러나 Swift는'& associatedObjectKey'가 항상 같은 포인터가 될 것이라고 보장하지 않습니다. 컴파일러가 이것을 보장하는 유일한 곳은 (비록 언어는 결코하지 않지만) 최상위 속성을위한 곳입니다. 이 정보는 컴파일러 엔지니어가 직접 제공합니다. –