안녕하세요. 후르륵짭짭 입니다.
요즘에 Objective-C에 대해서 공부하고 있는데,
가끔 Swift에서도 확인 할 수 있는 Selector 함수에 대해 설명을 해보려고 합니다.
Obective-C 프로젝트를 보면 정말 많이 사용되는 함수이기 때문에 알아두면 좋을 것 같습니다.
www.youtube.com/watch?v=MgsAqBd1HOQ
< 많이들 모르는 그룹이지만,,, 이 노래는 멜로디도 맘에 들고 특히 "계속 이러고 있자" 는 가사가 좋은것 같습니당 >
** Selector란 무엇인가 **
Select는 선택이라는 의미를 가지고 있습니다.
그래서 Selector는 함수를 선택해서 사용할 때 사용합니다.
Swift에는 Selector함수를 변수에 넣어서 사용할 수 있지만, Objective-C에서는 그것이 불가능합니다.
따라서 Selector함수가 등장 한 것 같습니다.
위에 내용 중에 다음 말이 가장 중요하다고 생각 됩니다.
What makes a selector useful is that (in conjunction with the runtime) it acts like a dynamic function pointer
Selector를 유용하게 만다는 것은 유동적인 함수 포인터로 행동한다는 것 입니다.
그럼 지금 부터 제대로 알아보도록 하겠습니다.
** 기본적인 사용방법 **
Selector를 함수를 선택하는 것이라 설명했습니다. 그리고 여러가지 방법이 있는데 이제 부터 알아보도록 하겠습니다.
- @selector(함수이름)를 사용하는 방법
@selector()에 함수이름을 적어주면 Selector가 생성 됩니다.
그리고 그 변수 이름은 "SEL" 입니다.
NSString *str = @"Hello world";
SEL s = @selector(uppercaseString);
NSString *ret = [str performSelector:s];
위에 처럼 string이 있고 대문자로 변경해주는 uppercaseString 함수를 사용하기 위해서는
@selector(uppercaseString)을 해주면 됩니다.
그러면 SEL이라는 변수에 Selector가 저장이되고 performSelector를 통해서 String의 uppercaseString을 실행 시켜줄 수 있습니다.
- String으로 사용하는 방법 -
SEL s3 = NSSelectorFromString(@"uppercaseString");
NSString *ret2 = [str performSelector:s3];
위에 처럼 NSSelectorFromString(함수이름 - 문자열)로 해주면 해당 문자열에 맞는 Selector 함수를 만들어 줍니다.
하지만 해당 함수가 없을 경우가 존재할 수도 있죠!
그럴 때를 대비해서 respondsToSelector 라는 메소드가 존재합니다.
SEL s3 = NSSelectorFromString(@"uppercaseString");
if([str respondsToSelector:s3]){
NSString *ret2 = [str performSelector:s3];
NSLog(@"Result : %@", ret);
}
else{
NSLog(@"셀렉터 호출 불가!");
}
이 메소드는 if else 문으로 해당 함수가 존재하는지 판단 해줍니다.
( 참고로 selector에서 함수이름을 적어 줄 때, 매개변수의 존재 유무에 따라 함수 이름 설정이 달라집니다. 매개변수가 존재하면 함수이름 앞에 " : " 을 붙여줘야합니다!!!!)
** 응용해보기 **
이번에는 직접 Class를 만든 후에 Selector 함수를 수행 하도록 하겠습니다.
SelectorClass.h를 만들겠습니다.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SelectorClass : NSObject
-(void) printHello;
-(NSString *) inputNprint : (NSString*) input;
-(BOOL) isSame : (int) a with : (int) b;
-(int) inputA : (int) a inputB : (int) b inputC : (int) c;
-(BOOL) printBOOL : (BOOL) input;
@end
NS_ASSUME_NONNULL_END
이렇게 여러가지 함수를 만들어준 이유는 다 이유가 있습니다.
그리고 다음 처럼 implemention을 만들었습니다.
-(void)printHello{
NSLog(@"Hello world~~!");
}
-(BOOL) printBOOL : (BOOL) input{
return input;
}
-(NSString *) inputNprint:(NSString *)input{
NSString * format = [NSString stringWithFormat:@"input is %@", input];
return format;
}
-(BOOL) isSame : (int) a with : (int) b {
return a == b ? YES : NO;
}
-(int) inputA : (int) a inputB : (int) b inputC : (int) c{
return a + b + c;
}
머 대충 이렇게 만들어주고 이제 본론을 시작하겠습니다.
SelectorClass *temp = [[SelectorClass alloc] init];
SEL s = @selector(inputNprint:);
NSString *result = [temp performSelector:s withObject:@"HARU"];
NSLog(@"%@", result);
이렇게 SelectorClass 객체를 만들어주고
@selector로 Selector 함수를 만들어줍니다.
그리고 performSelector를 수행하는데, withObject라는 것이 있습니다.
WithObject는 이제 매개변수로 값을 보내는 것인데,,,,
무조건 객체형태만 보낼 수 있습니다.... 또르륵
또한 최대 두개 밖에 못 보냅니다.... 쩝,,,,,
// SEL s2 = @selector(isSame:with:);
// int *result2 = [temp performSelector:s2 withObject:10 withObject:10];
// performSelector는 int 같은 변수를 받을 수가 없다.
그래서 위에 처럼 isSame:with: 함수는 사용 할 수 없습니다.
왜냐하면 int 형이기 때문입니다.
그리고 PerformSelector 메소드는 어디서 함수가 수행되는지 컴파일 과정에서 미리 예측 할 수 없기 때문에
메모리 누수가 발생할 확률이 높아서 경고 메시지가 나옵니다.
PerformSelector may cause a leak because its selector is unknown
** 단점을 극복한 방법 **
이런 단점을 극복한 방법 중 하는 IMP 를 사용하는 방법입니다.
SEL selectorString = NSSelectorFromString(@"printHello");
IMP doSomthing = [temp methodForSelector:selectorString];
doSomthing();
이렇게 Selector 함수를 만들고 methodForSelector 메소드로 IMP 를 만들어 줍니다.
그러면 동일하게 사용 할 수 있지만,,,, 이런 방법은 매개변수를 활용한 함수는 사용 할 수가 없습니다.
그래서 새로운 방식이 func 를 사용하는 겁니다!
이부분은 저도 확실하게 잘 알고 사용하는게 아니니깐, 보고 흘려도 좋습니다 ㅎㅎㅎ
SEL selectorString2 = NSSelectorFromString(@"inputNprint:"); //파라미터가 있다면 :을 꼭 붙여줘야한다.
IMP imp = [temp methodForSelector:selectorString2];
NSString * (* func)(id , SEL , NSString *) = (void *)imp;
NSString * result = func(temp , selectorString2 , @"Hururuek");
NSLog(@"%@", result);
이렇게 Selector 함수를 만들어 주고 IMP를 만들어 줍니다.
그리고 NSString 을 반환하고 (* func) (함수를 가진 객체 , 셀렉터 함수 , 매개변수 ) = (void * ) IMP 형식으로 작성 해줍니다.
이렇게 하면 하나의 함수가 생성이 되고 바로 다음 줄에 func(객체 , 셀렉터 , 인자) 이렇게 함수를 사용 할 수 있게 됩니다.
아주 좋죠?!!!
이렇게 하면 인자가 꼭 객체가 아니더라도 사용 할 수 있습니다.
SEL selectorString3 = NSSelectorFromString(@"isSame:with:");//이것도 파라미터가 두개라면 : : 을 해줘야한다.
IMP imp2 = [temp methodForSelector:selectorString3];
BOOL (* func2)(id, SEL, int , int ) =(void*)imp2;
BOOL result2 = func2(temp , selectorString3, 2, 2);
NSLog(@"%d", result2);
SEL selectorString4 = NSSelectorFromString(@"inputA:inputB:inputC:");
IMP imp4 = [temp methodForSelector:selectorString4];
int (* func4)(id, SEL , int, int ,int) = (void *)imp4;
BOOL result4 = func4(temp, selectorString4, 1, 2, 3);
NSLog(@"%d", result4);
이렇게 하면 경고 표시도 안나오고 하나의 Selector를 만들어서 동적으로 함수를 변환해서 사용이 가능해집니다.
- 21.05.01의 잡생각 -
예전에는 블로그 글을 작성 할 때, 그냥 작성 했는데 이제는
내 음악 취향을 공유하는 것 같아서 신중하게 음악 선택을 하게 되는,,,
근데 더 재미있음
참고 사이트
SEL / IMP 사용 :
stackoverflow.com/a/2650240/13065642
IMP에 대한 내용 :
riptutorial.com/objective-c/example/17811/imp--implementation-pointer-
Selector에 대한 내용 :
riptutorial.com/objective-c/example/16663/sel
'ETC. > 기타' 카테고리의 다른 글
(기타) 아이폰 13미니 정품 맥세이프 가죽 케이스 후기 (0) | 2022.06.26 |
---|---|
기타) Cordova Swift PlugIn 추가 (0) | 2021.10.24 |
기타) Objective-C VS Swift 어떤 차이가 있을까?? (0) | 2021.01.18 |
기타) Xcode 프로젝트 없이 백준 문제 풀기 (0) | 2020.12.30 |
기타 ) AutoLayOut을 StoryBoard VS Code 머가 날까? (5) | 2020.12.23 |
댓글