Swift

Hits

3. 오브젝티브-C vs 스위프트

오브젝티브-C는 20여 년 동안 애플의 주력 개발 언어로 사용된 만큼 튼튼한 아키텍처와 검증된 성능을 가지고 있다. 이런 오브젝티브-C를 대체할 목적으로 스위프트가 출현했을 때 많은 사람의 관심은 과연 스위프트가 오브젝티브-C를 대체할 수 있을까였다. 언어를 대체한다는 것은 기존 언어가 담당하던 모든 역할을 처리할 수 있는 역량이 되어야 가능하다. 이런 점에서 스위프트를 이야기할 때 오브젝티브-C와의 비교는 빠질 수 없다. 애플에 의해 탄생한, 형제라면 형제일 수 있는 두 언어 사이의 공통점과 차이점에 대해서 알아보자.


3.1 파일 통합

오브젝티브-C는 C를 기초로 하여 만들어진 언어이므로 파일 구조도 C 문법을 따라 헤더 파일과 소스 파일로 구분된다. .h 확장자로 작성되는 헤더 파일은 변수나 상수에 대한 선언, 인터페이스에 대한 정의가 작성되고, .m 확장자로 작성되는 소스 파일은 헤더 파일에서 정의한 인터페이스를 구현하는 내용이 작성된다. 선언과 구현이 분리되는 형태이다. 그러나 스위프트는 헤더 파일과 소수 파일이 모두 .swift 확장자를 갖는 파일 하나로 통합되어있다. 변수나 상수, 각종 객체의 형식에 대한 선언과 실질적인 내용 구현이 모두 하나의 파일에서 이루어진다.

3.2 클래스의 정의와 구현

클래스를 작성할 때 오브젝티브-C는 헤더 파일에 클래스의 인터페이스를 정의하고, 소스파일에서는 정의된 인터페이스를 구현한다. 그러나 스위프트에서는 클래스의 인터페이스 정의 없이 바로 클래스를 구현하면 된다. 또한 오브젝티브-C는 반드시 클래스 선언 시 반드시 상위 클래스를 상속 받아야 하며, 아무것도 상속받을 필요가 없을 때라도 최상위 클래스인 NSObject를 상속받아야 하지만, 스위프트에서는 상속받아야 할 클래스가 없다면 아무것도 상속받지 않아도 된다.

3.3 상속

오브젝티브-C는 다중 상속을 지원하지 않지만, 자바의 인터페이스(Interface)에 해당하는 개념인 프로토콜(Protocol)을 정의하여 클래스 객체가 준수해야 할 형식을 제공할 수 있다. 또한 카테고리(Category) 개념을 통해 상속 대신 기존 객체 자체를 직접 확장할 수 있다.

스위프트도 마찬가지이다. 다중 상속을 지원하지 않으며 프로토콜을 정의할 수있다. 또한, 기존 객체를 직접 확장할 수있도록 Extention이 제공되는데, 이는 오브젝티브-C의 카테고리에 대응하는 개념이다. 단, 그보다는 더 넓고 강력한 개념으로 오브젝티브-C에서 클래스 객체만 확장할 수 있었던 카테고리에 비해 스위프트의 Extention은 클래스, 구조체, 프로토콜 등 대부분 객체에 사용할 수 있다.

3.4 범용 타입

개발의 편의와 효율성을 높이기 위해 모든 데이터 타입을 저장할 수 있는 범용 타입 객체가 필요할 때가 있는데, 오브젝티브-C에서는 이와 같은 범용 타입으로 id 타입을 제공한다. id 타입은 모든 타입의 데이터를 저장할 수 있을 뿐만 아니라, 호환성만 보장된다면 저장된 데이터를 어떠한 타입으로든 변환할 수 있는 특성을 가지고 있다. 코코아 프레임워크나 코코아 터치 프레임워크에서는 범용 타입을 이용한 API들이 많이 사용되고 있다.

스위프트 역시 동일한 코코아 프레임워크나 코코아 터치 프레임워크를 사용하기 때문에, 오브젝티브-C의 id 타입에 대응하는 범용 타입의 객체가 필요하다. 이 때문에 제공되는 것이 Any 타입과 AnyObject 클래스이다. Any는 구조체, 클래스, 열거형, 함수 등 스위프트에서 제공하는 모든 타입의 값을 저장할 수 있는 타입인 반면, AnyObject는 클래스에 한해 범용으로 사용 가능한 데이터 타입이다.

3.5 메소드 호출

오브젝티브-C는 스몰토크의 문법을 차용한 결과, 메소드 호출을 메시지 전송 방식으로 처리한다. 즉, 객체의 메소드를 호출하는 대신 객체에 메시지를 보내서 필요한 기능을 처리한다. 객체와 메시지는 공백을 통해 연결되며 대괄호([])를 사용하여 메시지 전송 단위를 감싸서 구분한다. 이런 스몰토크의 문법이 낯설거나 익숙하지 않은 사람들이 많아서 오브젝티브-C를 다룰 때에는 종종 주의가 필요하기도 했다.

그러나 스위프트는 일반적인 객체지향에서의 메소드 호출 방식을 따른다. 객체와 메소드 사이는 점(.)을 통해 연결되고, 메소드 호출 단위를 감싸는 구분자는 사용하지 않는다. 아래는 오브젝티브-C와 스위프트에서의 메소드 호출 방식 예제이다.

  • 오브젝티브-C에서의 메소드 호출
    1
    
    [인스턴스명 increment:3]
    
  • 스위프트에서의 메소드 호출
    1
    
    인스턴스명.incrementBy(3)
    

3.6 nil의 의미

오브젝티브-C에서는 존재하지 않는 객체에 대한 참조를 위해 nil이라는 상수를 사용한다. nil과 NULL의 차이에 대해 궁굼해 하는 사람들이 있는데, 엄격하게 말해서 오브젝티브-C에서 정의된 nil 상수와 C에서 정의된 NULL 상수 간에 차이는 있지만, 오브젝티브-C 문법에서 두 상수는 기술적으로 혼용할 수 있다. 일반적으로 nil은 클래스 객체를 참조하는 데에 사용되고, NULL은 그 밖에 다른 포인터 자료형에 사용된다.

1
2
3
4
5
// 객체의 빈 참조에 사용되는 nil
UIViewController *uvc = nil;

// 포인터 자료형의 빈 참조에 사용되는 NULL
int *sPtr = NULL;

반면, 스위프트에서 nil은 옵셔널 타입의 기본값으로 사용되며 ‘값이 존재하지 않음’을 의마한다. 스위프트에서는 NULL 상수가 정의되어 있지 않다.

1
2
// 옵셔널 타입의 기본값으로 nil이 대입
var name : String? = nil

3.7 포인터 사용

오브젝티브-C는 C의 포인터 문법을 그대로 물려받았다. 객체에 대한 인스턴스 변수를 정의할 때에는 항상 포인터를 사용하는 레퍼런스 참조를 기본으로 사용했다. 모든 변수 앞에 포인터를 거의 의무적으로 붙여주다 보니, 오브젝티브-C에서 포인터를 사용한다고는 해도 C 코드를 직접 작성하는 부분을 제외하면 크게 신경 쓸 부분이 없기는 했지만, 그럼에도 포인터를 사용해야 한다는 것은 초보자들에게는 적지 않은 부담으로 작용했다.

스위프트에서는 이러한 포인터 개념을 제거하여 개발자가 직접 레퍼런스를 참조하지 않도록 하는 대신, 객체의 종류에 따라 컴파일러가 직접 레퍼런스를 참조할 것인지 아니면 객체를 복사할 것인지를 결정한다. 클래스틑 포인터를 사용하지 않아도 자동으로 레퍼런스를 참조하고, 구조체는 객체를 복사하여 사용하는 방식으로 처리된다.

3.8 객체지향 타입

오브젝티브-C는 객체지향을 위한 타입으로 클래스를 제공한다. @Interface 어노테이션을 이용하여 형식을 선언하고, @Implementation 어노테이션을 이용하여 실질적인 내용을 구현한다. 이렇게 작성된 클래스를 사용할 때에는 인스턴스를 생성하여 사용한다.

그러나 스위프트에서는 객체지향용 타입으로 클래스뿐만 아니라 구조체, 열거형까지 제공한다. 이들 객체 타입은 모두 인스턴스를 만들 수 있으며 인스턴스와 관련된 변수, 상수를 속성(property)으로 선언하여 사용할 수 있다. 그뿐만 아니라 이들 객체 타입에 인스턴스 메소드와 타입 메소드를 작성하여 사용할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
// 클래스 (Class)
class SampleClass {
}

// 구조체 (Structure)
struct SampleStruct {
}

// 열거형 (Enumeration)
enum SampleEnum {
}

3.9 익명 함수

현대 프로그래밍 개념에서 익명 함수의 사용은 람다 함수를 사용할 수 잇게 해주는 리스프, 스킴 같은 함수형 프로그래밍 언어로부터 도입되었다고 할 수 있다. 람다 함수는 함수 기반으로 정의되는 코드 내에서 한 번만 사용하면 되는 코드마저 함수로 선언해서 사용해야 하는 번거로움을 피할 수 있게 해줌으로써 코드를 더욱 간결하게 만들어 준다.

람다 함수는 최근 자바 8에서도 도입되는 등 프로그래밍 언어의 강력한 기능으로 고려되고 있다. 오브젝티브-C에서는 블록(Block)이라는 개념으로 익명 함수를 표현할 수 있었는데, 이 기능이 스위프트에서는 클로저를 이용한 익명 함수 정의 문법으로 제공된다.

3.10 오류 처리

일반적으로 객체 지향 언어에서 제공하는 오류 처리 기능은 오류 발생이 예상되는 지점에 미리 오류를 검출하는 코드를 작성해두고, 실제로 오류가 발생했을 때 정해진 코드 블록 바깥으로 오류 정보를 던져 처리할 수 있도록 지원하는 방식이다. 오브젝티브-C에서도 오류를 검출하기 위한 기능은 제공되었지만, 이는 읽고 쓸 수 있는 매개변수를 사용하여 오류를 검출해내는 방법이었을 뿐 오류 처리를 위한 구문이 별도로 제공된 것은 아니었다.

스위프트는 2.0 버전부터 오류를 검출해내고 각 오류에 효과적으로 대응할 수 있도록 전용 구문을 제공하고 있다. 많은 프로그래밍 언어에서 널리 사용하는 Try ~ Catch 구문을 채택한 스위프트는 코드를 실행하는 과정에서 오류가 발생하더라도 프로그램이 중단되는 것을 막아주고, 미리 준비된 대응 구문을 실행하여 효율적으로 오류에 대응할 수 있게 한다.



이전 내용 보기 다음 내용 보기

Leave a comment