개발관련/오류노트

[오류] performSelector may cause a leak because its selector is unknown

Rateye 2022. 1. 6. 14:46
728x90
반응형
질문 : performSelector는 선택자를 알 수 없기 때문에 누수를 일으킬 수 있습니다.

ARC 컴파일러에서 다음과 같은 경고가 표시됩니다.

"performSelector may cause a leak because its selector is unknown".

내가하는 일은 다음과 같습니다.

[_controller performSelector:NSSelectorFromString(@"someMethod")];

이 경고가 표시되는 이유는 무엇입니까? 컴파일러가 선택기가 존재하는지 여부를 확인할 수 없다는 것을 이해합니다. 그런데 왜 그로 인해 누수가 발생합니까? 이 경고가 더 이상 나타나지 않도록 코드를 어떻게 변경할 수 있습니까?

답변

컴파일러는 이유 때문에 이에 대해 경고합니다. 이 경고를 무시해야하는 경우는 매우 드물며 쉽게 해결할 수 있습니다. 방법은 다음과 같습니다.

if (!_controller) { return; }
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(_controller, selector);

또는 더 간결하게 (읽기 어렵고 경비원없이) :

SEL selector = NSSelectorFromString(@"someMethod");
((void (*)(id, SEL))[_controller methodForSelector:selector])(_controller, selector);

여기서 진행되는 것은 컨트롤러에 해당하는 메서드에 대한 C 함수 포인터를 컨트롤러에 요청하는 것입니다. 모든 NSObject class_getMethodImplementation methodForSelector: 응답하지만 Objective-C 런타임에서 class_getMethodImplementation을 사용할 수도 있습니다 id<SomeProto> 와 같은 프로토콜 참조 만있는 경우 유용함). 이러한 함수 포인터를 IMP 라고하며 간단한 typedef ed 함수 포인터 ( id (*IMP)(id, SEL, ...) ) 1 입니다. 이것은 메서드의 실제 메서드 시그니처에 가깝지만 항상 정확히 일치하지는 않습니다.

IMP 가 있으면 ARC에 필요한 모든 세부 정보를 포함하는 함수 포인터로 캐스팅해야합니다 (모든 Objective-C 메서드 호출 self_cmd 이것은 세 번째 줄에서 처리됩니다 ( (void *) 는 단순히 수행중인 작업을 알고 있고 포인터 유형이 일치하지 않기 때문에 경고를 생성하지 않음을 컴파일러에게 알려줍니다).

마지막으로 함수 포인터 2 를 호출합니다.

선택기가 인수를 받거나 값을 반환 할 때 약간 변경해야합니다.

SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [_controller methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = _controller ?
  func(_controller, selector, someRect, someView) : CGRectZero;

이 경고의 이유는 ARC를 사용하는 경우 런타임이 호출하는 메서드의 결과로 무엇을해야하는지 알아야하기 때문입니다. 결과는 무엇이든 될 수 있습니다 : void , int , char , NSString * , id 등. ARC는 일반적으로 작업중인 객체 유형의 헤더에서이 정보를 가져옵니다.

ARC가 반환 값에 대해 고려할 사항은 실제로 4 가지입니다. 4

methodForSelector: 호출은 호출하는 메서드의 반환 값이 개체라고 가정하지만 유지 / 해제하지 않습니다. 따라서 객체가 위의 # 3과 같이 해제되어야하는 경우 누수가 발생할 수 있습니다 (즉, 호출하는 메서드가 새 객체를 반환 함).

void 또는 기타 비 객체를 반환하는 선택기의 경우 경고를 무시하도록 컴파일러 기능을 활성화 할 수 있지만 위험 할 수 있습니다. Clang이 지역 변수에 할당되지 않은 반환 값을 처리하는 방법을 몇 번 반복하는 것을 보았습니다. methodForSelector: 에서 반환 된 객체 값을 유지하고 해제 할 수없는 이유가 없습니다. 사용하고 싶지 않더라도 말입니다. 컴파일러의 관점에서 보면 결국 객체입니다. 즉, 호출중인 메서드 someMethod 가 비 객체 ( void 포함)를 반환하면 가비지 포인터 값이 유지 / 해제되고 충돌 할 수 있습니다.

performSelector:withObject: 와 동일한 경고가 발생한다는 것입니다. 그리고 해당 메소드가 매개 변수를 사용하는 방법을 선언하지 않으면 유사한 문제가 발생할 수 있습니다. ARC는 소비 된 매개 변수 를 선언 할 수 있으며, 메소드가 매개 변수를 소비하면 결국 좀비에게 메시지를 보내고 충돌 할 수 있습니다. 브리지 캐스팅으로이 문제를 해결할 수있는 방법이 있지만 실제로는 IMP 및 함수 포인터 방법론을 사용하는 것이 좋습니다. 소비 된 매개 변수는 거의 문제가되지 않으므로 문제가되지 않을 것입니다.

흥미롭게도 컴파일러는 정적으로 선언 된 선택자에 대해 불평하지 않습니다.

[_controller performSelector:@selector(someMethod)];
  

그 이유는 컴파일러가 컴파일 중에 선택기와 객체에 대한 모든 정보를 실제로 기록 할 수 있기 때문입니다. 어떤 것에 대해 어떤 가정도 할 필요가 없습니다. (1 년 전에 출처를보고 확인했지만 지금은 참조가 없습니다.)

이 경고의 억제가 필요하고 좋은 코드 디자인이 필요한 상황을 생각하려고 할 때 나는 공백으로 떠올랐다. 누군가가이 경고를 무음으로 설정해야하는 경험이 있다면 공유해주세요 (위의 내용이 제대로 처리되지 않음).

NSMethodInvocation 을 빌드하는 것도 가능하지만 그렇게하려면 더 많은 타이핑이 필요하고 속도가 느리기 때문에 그렇게 할 이유가 거의 없습니다.

performSelector: 메소드 계열이 Objective-C에 처음 추가되었을 때 ARC는 존재하지 않았습니다. ARC를 만드는 동안 Apple은 개발자가 명명 된 선택기를 통해 임의의 메시지를 보낼 때 메모리 처리 방법을 명시 적으로 정의하기 위해 다른 수단을 사용하도록 안내하는 방법으로 이러한 메서드에 대해 경고를 생성해야한다고 결정했습니다. Objective-C에서 개발자는 원시 함수 포인터에서 C 스타일 캐스트를 사용하여이를 수행 할 수 있습니다.

Swift의 도입과 함께 Apple performSelector: 메소드 패밀리를 "내재적으로 안전하지 않음"으로 문서화했으며 Swift에서는 사용할 수 없습니다.

시간이 지남에 따라 우리는 다음과 같은 발전을 보았습니다.

그러나 이름이 지정된 선택기를 기반으로 메시지를 보내는 아이디어는 "본질적으로 안전하지 않은"기능이 아닙니다. 이 아이디어는 Objective-C와 다른 많은 프로그래밍 언어에서 오랫동안 성공적으로 사용되었습니다.

1 모든 Objective-C 메서드에는 메서드를 호출 할 때 암시 적으로 추가되는 self_cmd

2 NULL 함수를 호출하는 것은 C에서 안전하지 않습니다. 컨트롤러의 존재를 확인하는 데 사용되는 가드는 객체가 있는지 확인합니다. 그러므로 우리는 우리가 얻을 것이다 알고 IMP 에서 methodForSelector: (그것은있을 수 있지만 _objc_msgForward , 메시지 전달 시스템에 입력). 기본적으로 가드를 배치하면 호출 할 함수가 있다는 것을 압니다.

3 id 로 선언하고 모든 헤더를 가져 오지 않으면 잘못된 정보를 얻을 수 있습니다. 컴파일러가 괜찮다고 생각하는 코드에서 충돌이 발생할 수 있습니다. 이것은 매우 드물지만 발생할 수 있습니다. 일반적으로 두 가지 메서드 시그니처 중 어떤 것을 선택할지 모른다는 경고 만 받게됩니다.

4 자세한 내용은 유지 된 반환 값유지되지 않은 반환 값에 대한 ARC 참조를 참조하십시오.

출처 : https://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown
728x90
반응형