답변:
기본적으로 performSelector를 사용하면 주어진 객체에서 선택기를 호출 할 선택자를 동적으로 결정할 수 있습니다. 즉, 런타임 전에 선택기를 결정할 필요가 없습니다.
따라서 이것들이 동일하더라도 :
[anObject aMethod];
[anObject performSelector:@selector(aMethod)];
두 번째 양식을 사용하면 다음을 수행 할 수 있습니다.
SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];
메시지를 보내기 전에.
performSelector:
은 클래스에서 target-action을 구현할 때만 수행 할 수있는 작업입니다. 형제 자매 performSelectorInBackground:withObject:
와 performSelectorOnMainThread:withObject:waitUntilDone:
종종 더 유용합니다. 백그라운드 스레드를 생성하고 해당 백그라운드 스레드에서 주 스레드로 결과를 다시 호출합니다.
performSelector
컴파일 경고를 억제하는 데에도 유용합니다. 메서드가 존재한다는 것을 알고 있으면 (예 :을 사용한 후 respondsToSelector
) Xcode가 "에 응답하지 않을 수 있습니다 your_selector
." 라고 말하는 것을 중지 합니다. 경고의 실제 원인을 찾는 대신 사용하지 마십시오 . ;)
이 질문의 매우 기본적인 예를 들어,
[object doSomething];
[object performSelector:@selector(doSomething)];
무슨 일이 일어날 지에 차이가 없습니다. doSomething은 객체에 의해 동 기적으로 실행됩니다. "doSomething"은 아무 것도 반환하지 않고 매개 변수도 필요로하지 않는 매우 간단한 메서드입니다.
다음과 같이 조금 더 복잡한 것이 었습니까?
(void)doSomethingWithMyAge:(NSUInteger)age;
일이 복잡해질 것입니다. 왜냐하면 [object doSomethingWithMyAge : 42];
매개 변수가있는 모든 변형은 개체 매개 변수 만 허용하므로 더 이상 "performSelector"변형으로 호출 할 수 없습니다.
여기서 선택기는 "doSomethingWithMyAge :"이지만
[object performSelector:@selector(doSomethingWithMyAge:) withObject:42];
단순히 컴파일되지 않습니다. 42 대신 NSNumber : @ (42)를 전달하면 메서드가 객체가 아닌 기본 C 유형을 기대하기 때문에 도움이되지 않습니다.
또한 최대 2 개의 매개 변수에 대한 performSelector 변형이 있습니다. 메소드에는 여러 번 더 많은 매개 변수가 있습니다.
performSelector의 동기 변형이 있지만 다음과 같은 사실을 발견했습니다.
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
항상 객체를 반환하고 간단한 BOOL 또는 NSUInteger도 반환 할 수 있었고 작동했습니다.
performSelector의 두 가지 주요 용도 중 하나는 이전 답변에서 설명한대로 실행하려는 메서드의 이름을 동적으로 구성하는 것입니다. 예를 들면
SEL method = NSSelectorFromString([NSString stringWithFormat:@"doSomethingWithMy%@:", @"Age");
[object performSelector:method];
다른 용도는 현재 런 루프에서 나중에 실행될 객체에 메시지를 비동기 적으로 디스패치하는 것입니다. 이를 위해 몇 가지 다른 performSelector 변형이 있습니다.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector target:(id)target argument:(id)arg order:(NSUInteger)order modes:(NSArray *)modes;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
(예, NSThread, NSRunLoop 및 NSObject와 같은 여러 Foundation 클래스 범주에서 수집했습니다.)
각 변형에는 고유 한 동작이 있지만 모두 공통점을 공유합니다 (적어도 waitUntilDone이 NO로 설정된 경우). "performSelector"호출은 즉시 반환되며 객체에 대한 메시지는 잠시 후 현재 실행 루프에만 배치됩니다.
지연된 실행으로 인해-당연히 선택기의 메서드에서 사용할 수있는 반환 값이 없으므로 이러한 모든 비동기 변형에서-(void) 반환 값이 사용됩니다.
이걸 어떻게 든 다루었 으면 좋겠는데 ...
@ennuikiller가 자리 잡고 있습니다. 기본적으로 동적으로 생성 된 선택기는 코드를 컴파일 할 때 호출 할 메서드의 이름을 알지 못할 때 (일반적으로 알 수없는 경우) 유용합니다.
한 가지 주요 차이점은 -performSelector:
및 친구 ( 멀티 스레드 및 지연 변형 포함 )는 0-2 매개 변수가있는 메서드와 함께 사용하도록 설계되었다는 점에서 다소 제한적이라는 것입니다. 예를 들어 -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:
6 개의 매개 변수를 사용하여 호출 하고를 반환하는 NSString
것은 매우 다루기 어렵고 제공된 메서드에서 지원되지 않습니다.
NSInvocation
개체 를 사용해야 합니다.
performSelector:
친구들은 모두 객체 인자를받습니다. 즉 setAlphaValue:
, 인자가 float이기 때문에 (예를 들어) 호출하는 데 사용할 수 없습니다 .
선택기는 다른 언어의 함수 포인터와 약간 비슷합니다. 컴파일 타임에 어떤 메서드를 런타임에 호출할지 모를 때 사용합니다. 또한 함수 포인터와 마찬가지로 호출의 동사 부분 만 캡슐화합니다. 메서드에 매개 변수가있는 경우 매개 변수도 전달해야합니다.
An NSInvocation
은 더 많은 정보를 결합한다는 점을 제외하고는 유사한 목적을 수행합니다. 동사 부분뿐만 아니라 대상 개체 및 매개 변수도 포함합니다. 이것은 현재가 아니라 미래에 특정 매개 변수를 사용하여 특정 개체에 대한 메서드를 호출하려는 경우에 유용합니다. 적절한 것을 구축하고 NSInvocation
나중에 해고 할 수 있습니다 .
둘 사이에는 또 다른 미묘한 차이가 있습니다.
[object doSomething]; // is executed right away
[object performSelector:@selector(doSomething)]; // gets executed at the next runloop
다음은 Apple 문서에서 발췌 한 것입니다.
"performSelector : withObject : afterDelay : 다음 실행 루프주기 동안 및 선택적 지연 기간 후에 현재 스레드에서 지정된 선택기를 수행합니다. 선택기를 수행하기 위해 다음 실행 루프주기까지 대기하기 때문에 이러한 메서드는 자동으로 최소 지연을 제공합니다. 현재 실행중인 코드입니다. 대기열에있는 여러 선택기가 대기열에있는 순서대로 차례로 수행됩니다. "
performSelector:withObject:afterDelay:
것이지만 질문과 스 니펫은 performSelector:
완전히 다른 방법 인를 사용하고 있습니다 . 문서에서 : <quote>이 performSelector:
방법은 aSelector
수신자에게 직접 메시지 를 보내는 것과 동일합니다 . </ quote>
performSelector/performSelector:withObject/performSelector:withObject:afterDelay
모두가 같은 방식으로 행동 했다고 생각했는데 실수였다.