-performSelector 사용 : 메소드 호출과 비교


답변:


191

기본적으로 performSelector를 사용하면 주어진 객체에서 선택기를 호출 할 선택자를 동적으로 결정할 수 있습니다. 즉, 런타임 전에 선택기를 결정할 필요가 없습니다.

따라서 이것들이 동일하더라도 :

[anObject aMethod]; 
[anObject performSelector:@selector(aMethod)]; 

두 번째 양식을 사용하면 다음을 수행 할 수 있습니다.

SEL aSelector = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector: aSelector];

메시지를 보내기 전에.


3
실제로 findTheAppropriateSelectorForTheCurrentSituation ()의 결과를 aSelector에 할당 한 다음 [anObject performSelector : aSelector]를 호출한다는 점을 지적 할 가치가 있습니다. @selector는 SEL을 생성합니다.
Daniel Yankowsky

4
사용 performSelector:은 클래스에서 target-action을 구현할 때만 수행 할 수있는 작업입니다. 형제 자매 performSelectorInBackground:withObject:performSelectorOnMainThread:withObject:waitUntilDone:종종 더 유용합니다. 백그라운드 스레드를 생성하고 해당 백그라운드 스레드에서 주 스레드로 결과를 다시 호출합니다.
PeyloW

2
performSelector컴파일 경고를 억제하는 데에도 유용합니다. 메서드가 존재한다는 것을 알고 있으면 (예 :을 사용한 후 respondsToSelector) Xcode가 "에 응답하지 않을 수 있습니다 your_selector." 라고 말하는 것을 중지 합니다. 경고의 실제 원인을 찾는 대신 사용하지 마십시오 . ;)
Marc

저는 StackOverflow의 다른 스레드에서 performSelector를 사용하는 것이 끔찍한 디자인을 반영한 것이라고 읽었으며 많은 엄지를 받았습니다. 다시 찾을 수 있으면 좋겠어요. Google을 검색하여 결과를 stackoverflow로 제한하고 18,000 개의 결과를 얻었습니다. 으으.
Logicsaurus Rex 2015

"끔찍한 디자인의 반영"은 지나치게 단순합니다. 이것은 블록이 사용 가능하기 전에 우리가 가졌던 것이었고, 당시 또는 지금 모든 용도가 나쁜 것은 아닙니다. 이제 블록 사용할 수 있지만 매우 간단한 작업을 수행하지 않는 한 새 코드에 더 나은 선택 일 것입니다.
Ethan

16

이 질문의 매우 기본적인 예를 들어,

[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) 반환 값이 사용됩니다.

이걸 어떻게 든 다루었 으면 좋겠는데 ...


12

@ennuikiller가 자리 잡고 있습니다. 기본적으로 동적으로 생성 된 선택기는 코드를 컴파일 할 때 호출 할 메서드의 이름을 알지 못할 때 (일반적으로 알 수없는 경우) 유용합니다.

한 가지 주요 차이점은 -performSelector:및 친구 ( 멀티 스레드 및 지연 변형 포함 )는 0-2 매개 변수가있는 메서드와 함께 사용하도록 설계되었다는 점에서 다소 제한적이라는 것입니다. 예를 들어 -outlineView:toolTipForCell:rect:tableColumn:item:mouseLocation:6 개의 매개 변수를 사용하여 호출 하고를 반환하는 NSString것은 매우 다루기 어렵고 제공된 메서드에서 지원되지 않습니다.


5
그러려면 NSInvocation개체 를 사용해야 합니다.
Dave DeLong

6
또 다른 차이점 : performSelector:친구들은 모두 객체 인자를받습니다. 즉 setAlphaValue:, 인자가 float이기 때문에 (예를 들어) 호출하는 데 사용할 수 없습니다 .
Chuck

4

선택기는 다른 언어의 함수 포인터와 약간 비슷합니다. 컴파일 타임에 어떤 메서드를 런타임에 호출할지 모를 때 사용합니다. 또한 함수 포인터와 마찬가지로 호출의 동사 부분 만 캡슐화합니다. 메서드에 매개 변수가있는 경우 매개 변수도 전달해야합니다.

An NSInvocation은 더 많은 정보를 결합한다는 점을 제외하고는 유사한 목적을 수행합니다. 동사 부분뿐만 아니라 대상 개체 및 매개 변수도 포함합니다. 이것은 현재가 아니라 미래에 특정 매개 변수를 사용하여 특정 개체에 대한 메서드를 호출하려는 경우에 유용합니다. 적절한 것을 구축하고 NSInvocation나중에 해고 할 수 있습니다 .


5
함수 포인터는 인수로 호출 할 수 있고 셀렉터를 사용하여이를 구현하는 모든 개체에서 특정 메서드를 호출하는 데 사용할 수 있다는 점에서 선택자는 실제로 함수 포인터와 전혀 다릅니다. 선택자는 함수 포인터와 같은 전체 호출 컨텍스트를 갖지 않습니다.
bbum

1
선택자는 함수 포인터와 같지 않지만 여전히 비슷하다고 생각합니다. 그들은 동사를 나타냅니다. C 함수 포인터는 동사를 나타냅니다. 추가 컨텍스트 없이는 둘 다 유용하지 않습니다. 선택기에는 개체와 매개 변수가 필요합니다. 함수 포인터에는 매개 변수 (작동 할 개체가 포함될 수 있음)가 필요합니다. 내 요점은 필요한 모든 컨텍스트를 포함하는 NSInvocation 객체와 어떻게 다른지 강조하는 것이 었습니다. 제 비교가 혼란 스러웠던 것 같습니다.이 경우 사과드립니다.
Daniel Yankowsky

1
선택기는 함수 포인터가 아닙니다. 근처에도 안. 실제로는 메서드의 "이름"을 포함하는 단순한 C 문자열입니다 ( '함수'와 반대 임). 매개 변수 유형을 포함하지 않기 때문에 메서드 시그니처도 아닙니다. 객체는 동일한 선택자 (다른 매개 변수 유형 또는 다른 반환 유형)에 대해 둘 이상의 메소드를 가질 수 있습니다.
Motti Shneor 2014 년

-7

둘 사이에는 또 다른 미묘한 차이가 있습니다.

    [object doSomething]; // is executed right away

    [object performSelector:@selector(doSomething)]; // gets executed at the next runloop

다음은 Apple 문서에서 발췌 한 것입니다.

"performSelector : withObject : afterDelay : 다음 실행 루프주기 동안 및 선택적 지연 기간 후에 현재 스레드에서 지정된 선택기를 수행합니다. 선택기를 수행하기 위해 다음 실행 루프주기까지 대기하기 때문에 이러한 메서드는 자동으로 최소 지연을 제공합니다. 현재 실행중인 코드입니다. 대기열에있는 여러 선택기가 대기열에있는 순서대로 차례로 수행됩니다. "


1
귀하의 답변은 사실이 아닙니다. 인용 한 문서는에 관한 performSelector:withObject:afterDelay:것이지만 질문과 스 니펫은 performSelector:완전히 다른 방법 인를 사용하고 있습니다 . 문서에서 : <quote>이 performSelector:방법은 aSelector수신자에게 직접 메시지 를 보내는 것과 동일합니다 . </ quote>
jscs

3
설명을 해주신 Josh에게 감사드립니다. 당신이 올바른지; 나는 performSelector/performSelector:withObject/performSelector:withObject:afterDelay모두가 같은 방식으로 행동 했다고 생각했는데 실수였다.
avi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.