반 객체지향(Half-of-OOP)
가장 먼저 명심하자. 오브젝티브-C는 C언어의 슈퍼셋(Super Set)이고 C++의 객체 지향과는 거리가 멀다. 오브젝티브-C 언어는 마치 객체지향 프로그래밍(OOP, Object-Oriented Programming) 처럼 보이지만 객체지향 언어가 아니다. 그렇다고 완전히 함수형 언어도 아니다.
하지만, 오브젝티브-C는 객체지향의 가장 대표적인 특징인 상속(Inheritence)이 가능하고 인터페이스 구현이 가능하다. ANSI-C 입장에서 바라보면 상속과 인터페이스 구현은 함수형 언어로서 가당치도 않은 언어적 특성임에 틀림 없을 것이다. (이 문장의 인터페이스는 오브젝티브-C의 @protocol을 의미함.)
물론, C 언어에서도 구조체(struct), 포인터(pointer) 등을 조합하여 객체처럼 다룰 수 있지만, 오브젝티브-C 처럼 상속과 인터페이스의 구현은 객체지향 언어가 아님에도 불구하고 완전히 객체지향 프로그래밍 처럼 개발하는데 모자람이 없을 지경이다.
정리해 보면 오브젝티브-C의 객체지향 특징은 다음과 같다.
1. 상속성(Inheritance)
상속성은 부모의 특징을 물려 받음과 동시에 재정의(override), 부모 호출(super 또는 base)를 지원한다. 더 자세히 말할 필요는 없을 것 같아서 걍~ 넘어간다.
// 오브젝티브-C 의 상속
@interface MFAppController : NSObject
{
// .. 생략 ..
}
2. @protocol - 인터페이스 구현
오브젝티브-C의 프로토콜(protocol)은 C#과 Java의 interface와 같지만, 한 가지 다른 점은 인터페이스에 정의된 메서드 구현이 필수가 아니라는 점이다. 프로토콜의 모든 메서드를 굳이 구현하기 싫으면 하지 않아도 오브젝티브-C는 너그럽게 용서해 준다.
어떻게 이런 것이 가능할까? 지난 아티클 ‘[Objective-C] 아름다운을 추구하는 오브젝티브-C 언어 1/N’ 에서 다룬 것 처럼 메시지를 기반으로 메서드 호출을 위임하는 언어적인 특성 때문에 가능하다. 모든 프로토콜의 메서드를 정의하든 말든 컴파일러는 컴파일 할 뿐이고, 런타임에서 메시지를 통해 메서드를 호출해서 메서드가 구현되지 않았으면 예외를 발생하던가 아니면 씹던가 하게 된다. (단, @optional과 @required 참고)
// 오브젝티브-C 프로토콜 및 다중 상속
@protocol Animal
- (void)say;
@end
@protocol Duck <Animal, NSObject>
- (void)swim;
@end
3. @category - 확장 메서드
카테고리(@category)는 C#에서의 확장 메서드(Extension Methods)와 똑같다. 확장 메서드는 2008년 C# 3.0에 언어적인 특징으로 포함되었다. 이와 더불어 람다 표현식(Lambda Expression), 이 두 가지는 C# 코드로 표현한 라인 수를 급격히 줄일 수 있었고, 더 섬세한 객체지향 프로그램에 집중할 수 있게 해준다.
@interface NSArray (ConvertableArray)
- (NSDictionary*)ConvertToDictionary;
@end
@implementation NSArray (ConvertableArray)
- (NSDictionary*)ConvertToDictionary
{
// .. 실제 구현 생략 ..
return nil;
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSArray* arr = [NSArray arrayWithObjects:@"A", @"B", nil];
NSDictionary* dic = [arr ConvertToDictionary];
// TODO
}
return 0;
}
같은 코드로 C# 으로 변환하면 다음 코드와 같다.
public static class ConvertableArray
{
public static IDictionary ConvertToDictionary(this IList list)
{
// .. 구현 생략 ..
return null;
}
}
class MainClass
{
public static void Main (string[] args)
{
var arr = new List<String> { "A", "B" };
var dic = arr.ConvertToDictionary ();
}
}
하지만, 내부적으로 처리되는 매커니즘은 완전히 틀리다. 무엇이 그렇게도 완전히 틀릴까?
- C# 확장 메서드는 정적 클래스의 정적 메서드로만 표현할 수 있다.
- 오브젝티브-C 카테고리는 인스턴스 메서드이다.
그러므로 오브젝티브-C의 카테고리(@category)는 C#의 확장 메서드와 달리 인스턴스화 된 카테고리 메서드가 호출될 때 마다 참조 카운터가 증가한다. NSArray
객체가 인스턴스화 되었기 때문에 카테고리 메서드를 호출할 수 있다는 이야기가 된다.
카테고리의 메서드는 디버거를 통해 다음과 같이 역어셈블리 된다. 역어셈블리 코드를 좀 더 보기 편하기 위해 로컬 변수를 선언하여 살펴 보았다.
먼저 48만큼 메모리 사이즈를 확보한 다음 넘어온 값과 로컬 변수를 초기화하거나 값을 대입한다. rbp-8( =rdi)
은 self
가 들어간 것이고, rbp-16 (=rsi)
는 메서드 정보가 들어간다. rbp-20 (=$10)
은 INT32 정수 값이 할당된다. 마지막으로 +48로 원래 스택 포인터로 돌아와 빈 값을 리턴하게 된다. 스택이 8씩 자라는 것은 필자가 소스코드를 64비트 대상으로 컴파일 했기 때문이다.
0x100000d40: pushq %rbp
0x100000d41: movq %rsp, %rbp
0x100000d44: subq $48, %rsp
0x100000d48: movq %rdi, -8(%rbp)
0x100000d4c: movq %rsi, -16(%rbp)
0x100000d50: movl $10, -20(%rbp)
0x100000d57: movq -8(%rbp), %rsi
0x100000d5b: movq %rsi, %rdi
0x100000d5e: callq 0x100000eac ; symbol stub for: objc_retain
0x100000d63: leaq -32(%rbp), %rdi
0x100000d67: movabsq$0, %rsi
0x100000d71: movq %rax, -32(%rbp)
0x100000d75: movl $1, -36(%rbp)
0x100000d7c: callq 0x100000eb8 ; symbol stub for: objc_storeStrong
0x100000d81: movabsq$0, %rax
0x100000d8b: addq $48, %rsp
0x100000d8f: popq %rbp
0x100000d90: ret

댓글을 달아 주세요
안녕하세요. 서버관리자 입니다. 제가 관리하는 서버가 BASH 취약점이 있는데요.
외부적으로 포트를 열수가 없어서 수동으로 bash 쉘을 패치해야 하는데
어떻게 해야 하는지 알려주시면 감사하겠습니다.
부탁드립니다.
RedHat, CentOS 일 경우
rpm -Uvh bash............. 하시면 되요
좋은 정보 고맙습니다. ^^
안녕하세요. 영문페이지로가서 그대로 설치하였더니.....
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test" 명령어를 입력하면 결과에
sh: error importing function definition for `BASH_FUNC_module()'
위에 오류가 납니다.. 모듈이 Import되어 있지 않다고 나오는것 같은데. 혹시 이런 증상 해결 방법이 있을까요? 쉘스크립트가 정상적으로 실행이 되는지 테스트 해봐야겠네요..
저도 처음 보는 오류 메시지네요.
혹시 해결하시면 결과 공유 부탁 드릴게요.