2013-08-31 1 views
1

배경 : 서버와의 연결을 나타내는 개체가 있습니다 (BackendClient). 그것의 메서드는 단일 @protocol 생성되며 그들은 모두 동기, 그래서 그들을 배경으로 그들을 호출합니다 프록시 개체를 만들고 싶습니다. 주요 문제는 반환 값입니다. 분명히 비동기 메서드에서 반환 할 수 없으므로 콜백을 전달해야합니다. "쉬운"방법은 모두 BackendClient의 메소드를 복사하고 콜백 인수를 추가하는 것입니다. 그러나 ObjectiveC 자연은 동적 인 반면, 그것은 그 문제를 해결하는 매우 역동적 인 방법은 아닙니다. 여기에 performSelector:이 표시됩니다. 문제를 완전히 해결하지만 프록시 객체 투명성을 거의 없애 버립니다.performSelector :없이 선언 된 선택기를 보내는 방법

문제 : 이미 선언 된 것처럼 프록시 (NSProxy의 서브 클래스) 오브젝트되지 선언 선택기를 보낼 수 있도록하려면 .

-(AuthResponse)authByRequest:(AuthRequest*)request 

프로토콜 BackendClient의 예를 들면 다음과 같습니다 , 나는 방법이있다. 그리고이 같은 프록시 호출보기 원하는 :

[proxyClient authByRequest:myRequest withCallback:myCallback]; 

을하지만이 때문에

No visible @interface for 'BackendClientProxy' declares the selector 'authByRequest:withCallBack:'

확인을 컴파일하지 않을 것입니다. 컴파일러를 잠시 진정 시키자.

[(id)proxyClient authByRequest:myRequest withCallback:myCallback]; 

Awww. 또 다른 오류 :

No known instance method for selector 'authByRequest:withCallBack:'

내 마음에 오는이 시점 어떻게 든 런타임에 필요한 방법과 새로운 @protocol를 구성하지만, 내가 어떻게 그렇게 할 생각이없는 유일한 것은.

결론 :이 컴파일 오류를 억제해야합니다. 그걸 어떻게 할 수 있을지 생각해?

+1

매트 - 프로그래밍/다이나믹 런타임 토끼 구멍에 깊이 빠져있는 것 같습니다. ObjC는 SmallTalk에서 뿌리를 내리고 그 의지로 구부릴 수 있지만, 실제로는 자연스럽게 적합하지 않습니다. 일반적으로'performSelector :'또는 프록시의 과도한 사용은 시간이 지남에 따라 유지하기가 어렵 기 때문에 * 코드 냄새 *로 간주됩니다. – bbum

+0

그렇다면 클라이언트 메소드를 비동기 랩으로 처리하라는 올바른 방법은 무엇일까요? 그냥 모든 메소드를 복사해서 콜백 인자를 추가하면됩니까? – folex

+0

bbum는 사실입니다! – CouchDeveloper

답변

0

NSSelectorFromString()을 사용할 수 있습니다, 런타임에 선택기를 조회하려면,하지만이 경우 당신은 가서해야하고

+0

이 경우 string에서 selector를 생성하는 것은'performSelector :'와 같을 것이다. 그리고 네,'authByRequest :'selector를 얻을 수 있지만'authByRequest : withCallback :'이 필요합니다. – folex

1

내가 알고있는 경우 -authByRequest:의 선언을 얻을 필요가 어떤 헤더 수입, 당신은이 당신이 등, 메인 이벤트 루프, 말, 차단하지의 목적을 위해 비동기하려는 동기 비 스레드, API ...

나는 BackgroundClient 직렬 큐를 추가 :

@property(strong) dispatch_queue_t serialQueue; 

... somewhere in your -init ... 
_serialQueue = dispatch_queue_create(..., serial constant); 

티 암탉 : 다시 작성하거나 무겁게 리팩토링없이 비동기 모델에 BackgroundClient를 이동하는 가장 쉬운 방법입니다

[myClient dispatchOperation:^{ 
     [myClient doSynchronousA]; 
     id result = [myClient doSynchronousB]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [someone updateUIWithResult:result]; 
     } 
}]; 

:

- (void)dispatchOperation:(dispatch_block_t)anOperation 
{ 
     dispatch_async(_serialQueue, anOperation); 
} 

처럼 사용할 수있다.

API를 강화하려면 클라이언트 인스턴스와 직렬 대기열을 보유하는 BackendClient의 클래스 래퍼를 만듭니다.해당 클래스가 클라이언트와 코드의 나머지 부분을 인스턴스화하도록합니다. 만 해당 래퍼에서 인스턴스를 검색합니다. 이 경우에도 동일한 dispatchOperation: 모델을 사용할 수 있지만 모든 방법을 미러링 할 필요는 없습니다.


타입 정의 공극 (^ AsyncBackendBlock (BackendClient * BC) @interface의 AsyncBackend + (instancetype) asyncBackendWithBackend (BackendClient *) BC;

@property .... serialQueue; 

- (void) dispatchAsync:(AsyncBackendBlock) backBlock; 
@end 

하는 .m :

@interface AsyncBackend() 
@property... BackendClient *client; 
@end 

@implementation AsyncBackend 

- (void) dispatchAsync:(AsyncBackendBlock) backBlock 
{ 
    dispatch_async(_serialQueue, ^{ 
     backBlock(_client); 
    }); 
} 
@end 

발신자 :

AsyncBackend *b = [AsyncBackend asyncBackendWithBackend:[BackendClient new]]; 
[b dispatchAsync:^(BackendClient *bc) { 
    [bc doSomething]; 
    id result = [bc retrieveSomething]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [uiThingy updateWithResult:result]; 
    } 
}]; 
.... 
+0

나는 마지막 2 문장을 얻지 못한다. 자, 그런 래퍼가 있다고 가정 해 봅시다. 'BackendClientWrapper'라고 부르 자요, 거기에'client' 속성이 있습니다. 자, 어떻게 백엔드 메소드를 호출할까요? – folex

+0

이제 알겠습니다. 원하는 곳에이 대기열을 보관할 수 있습니다. 음, 옵션 주셔서 감사합니다! 슬픈 나는 그것을 투명하게 할 수 없다. – folex