42. GCD를 많이 사용하고performSelector 시리즈를 적게 사용합니다
제42조: GCD를 많이 사용하고performSelector 시리즈를 적게 사용하는 방법
Objective-C는 본질적으로 매우 동적 언어(제11조 참조)로 NSObject는 개발자가 임의로 어떤 방법을 호출할 수 있도록 몇 가지 방법을 정의했다.이 몇 가지 방법은 실행 방법의 호출을 연기할 수도 있고, 실행 방법에 사용할 라인을 지정할 수도 있다.이런 기능들은 원래 매우 유용했지만 대중추 파발과 블록 같은 신기술이 등장한 후에는 그다지 필요하지 않게 보였다.비록 몇몇 코드는 여전히 그것들을 자주 사용하지만, 필자는 너에게 피하는 것이 좋겠다고 충고한다.
이 중 가장 간단한 것은 "performSelector:"입니다.이 방법은 직접 선택 서브를 호출하는 것과 같은 효과가 있다.따라서 다음 두 줄 코드의 실행 효과는 같다.
[self performSelector:@selector(selectorName)];
[self selectorName];
이런 방식은 보기에 쓸데없는 것 같다.만약 선택자가 운행 기간에 결정된다면 이 방식의 강점을 나타낼 수 있을 것이다.이것은 동적 바인딩 위에 동적 바인딩을 다시 사용하는 것과 같으므로 다음과 같은 기능을 사용할 수 있습니다.
SEL selector;
if (/* some condition */) {
selector = @selector(foo);
}else if (/* some other condition */){
selector = @selector(bar);
}else{
selector = @selector(baz);
}
[object performSelector:selector];
이런 프로그래밍 방식은 매우 유연해서 자주 복잡한 코드를 간소화하는 데 쓸 수 있다.선택자를 먼저 저장하고 어떤 사건이 발생한 후에 호출하는 방법도 있다.어떤 사용법을 사용하든지 간에 컴파일러는 실행할 선택자가 무엇인지 모른다. 이것은 실행 기간이 되어야 확정할 수 있다.그러나 이 기능을 사용하는 대가로 ARC에서 코드를 컴파일하면 컴파일러가 다음과 같은 경고 메시지를 보냅니다.
warning:PerformSelector may cause a leak because its selector is unknown
너는 아마 이런 경고가 나타날 것이라고 예상하지 못했을 것이다.이 소식은 이상하게 보일 수도 있고, 왜 메모리 유출 문제가 언급되었는지 궁금하다.왜냐하면 컴파일러가 호출할 선택자가 무엇인지 모르기 때문에 그 방법의 서명과 반환 값도 모르고 심지어 반환 값이 있는지도 모른다.그리고 컴파일러가 방법명을 모르기 때문에 ARC의 메모리 관리 규칙을 활용하여 반환값이 방출되어야 하는지 아닌지를 판정할 수 없다. 이를 감안하여 ARC는 비교적 신중한 방법으로 방출 조작을 추가하지 않는다.그러나 이렇게 하면 메모리 유출을 초래할 수 있다. 왜냐하면 방법은 대상을 되돌릴 때 이미 그것을 보류했기 때문이다.
다음 코드를 고려하십시오.
SEL selector;
if (/* some condition */) {
selector = @selector(newObject);
}else if (/* some other condition */){
selector = @selector(copy);
}else{
selector = @selector(someProperty);
}
id ret = [object performSelector:selector];
만약 두 개의 선택자 중 하나를 호출한다면,ret 대상은 이 코드로 방출해야 하고, 세 번째 선택자라면 방출할 필요가 없다.ARC 환경에서뿐만 아니라 비 ARC 환경에서도 이렇게 해야 방법의 명명 규범을 엄격히 따랐다고 할 수 있다.ARC를 사용하지 않으면 (이 때 컴파일러도 경고 메시지를 보내지 않습니다) 앞의 두 가지 상황에서ret 대상을 수동으로 풀어야 하고, 뒤의 두 가지 상황에서는 풀어야 합니다.이 문제는 무시하기 쉬우며 정적 분석기를 사용해도 뒤이어 메모리 유출을 탐지하기 어렵다.performSelector 시리즈의 방법을 조심스럽게 사용해야 하는 이유 중 하나다.
이러한 방법은 그다지 이상적이지 않다. 또 다른 원인은 되돌아오는 값이void나 대상 유형일 뿐이라는 데 있다.실행할 선택자도void를 되돌릴 수 있지만,performSelector 방법의 되돌림 값 형식은 id입니다.정수나 부동점수 등 유형의 값을 되돌리려면 복잡한 변환 작업을 실행해야 하는데, 이런 변환은 틀리기 쉽다.id 유형은 임의의 Objective-C 대상을 가리키는 바늘을 표시하기 때문에 기술적으로 되돌아오는 값의 크기와 바늘이 차지하는 크기만 같으면 된다.반환 값의 유형이 C 언어의 구조체라면 performSelector 방법을 사용할 수 없습니다.
performSelector는 메시지를 보낼 때 매개 변수를 전달할 수 있는 다음과 같은 버전도 있습니다.
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
이런 방법들은 보기에는 유용한 것 같지만, 사실은 한계가 꽤 많다.매개 변수 형식이 id이기 때문에 들어오는 매개 변수는 대상이어야 합니다.만약 하위 수락의 매개 변수가 정수나 부동점수라면 이러한 방법을 사용할 수 없습니다.또한 하위 선택은 최대 두 개의 매개 변수만 받아들일 수 있지만, 매개 변수가 두 개가 아닌 경우, 대응하는performSelector 방법이 이 하위 선택을 실행할 수 없습니다.
performSelector 시리즈 방법은 선택자를 연기하거나 다른 라인에 놓고 실행할 수 있는 기능도 있습니다.
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
그러나 이 방법들은 너무 제한적이라는 것을 곧 알게 될 것이다.만약 이러한 방법을 사용하려면 많은 매개 변수를 사전에 포장한 다음 호출된 방법에서 추출해야 한다. 그러면 비용이 증가하고 버그가 발생할 수도 있다.
만약 다른 대체 방안으로 바꾸면 이러한 제한을 받지 않을 것이다.가장 주요한 대체 방안은 블록을 사용하는 것이다(제37조 참조).그리고performSelector 시리즈 방법이 제공하는 스레드 기능은 모두 대중추 발송 메커니즘에서 블록을 사용하여 실현할 수 있다.지연 실행은 dispatchafter는 다른 라인에서 작업을 수행하면dispatchsync 및 dispatchasync로 이루어집니다.
예를 들어, 다음 작업을 연기합니다.
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
[self doSomething];
});
주 스레드에서 작업을 수행하려면 다음과 같이 하십시오.
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomething];
});
요점
4
4
4
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.