Swifter {Swift Developer}

메뉴

클래스에 대한 개념잡기

[이번 강좌 주요 목표]

  • 클래스 이해하기
  • 속성 이해하기
  • 이니셜라이저 이해하기
  • 메소드 이해하기
  • 액세스 한정자 이해하기

 

  1. 클래스

(1) 객체지향

객체지향은 데이터집합 및 데이터 처리를 객체라는 단일단위로 관리하는 것이다. ‘회사원’이라는 개체를 예로 설명하겠다. 일반적으로 “사원번호”, “이름”, “직책”등의 데이터가 있을 수 있다. 이런 데이터를 통해 직책을 얻는 처리 기능을 제공하여 실행시 회사원객체는 자신이 가지고 있는 직책 데이터를 리턴한다.

회사원 홍길동을 나타내는 개체는 직책을 얻는 처리를 실행하면 ‘대리’라는 직책데이터를 리턴되는 형태이디ㅏ.

 

클래스

[속성, 메소드, 이니셜라이저]

 

위에서 이야기했던 회사원을 가지고 클래스를 만든다고 보면, 사원번호, 이름, 직책이라는 데이터 속성으로 직책을 표시하는 기능을 메소드로 정의한다. 이니셜라이저는 클래스 초기화하는 기능이다.

 

class 클래스명 {

이니셜라이저

속성정의

메소드 정의

}

 

기본적으로 클래스정의만으로 사용할 수 없다. 클래스에서 실체를 작성하고 처음 사용할 수 있다. 클래스 실체를 만드는 작업은 건축설계와도 비슷하다. 집을 지을때 설계도가 필요하다. 설계도는 실제 집에서는 필요가 없지만 설계도를 기초로 집을 짓고 처음 살 수 있게 된다. 클래스도 비슷하다. 클래스 정의가 집설계도에 해당 클래스 정의에서 생성된 개체가 실제 집에 해당된다.

설계도가 1장이 있으면 파란집, 노란집처럼 약간의 특징이 다른 집을 여러개 만들 수 있다. 클래스도 마찬가지로 하나의 클래스 정의에서 여러 개체를 만들 수 있다. 또한 클래스에서 생성된 객체를 인스턴스라고 하며 클래스에서 인스턴스를 만드는 것을 인스턴스 생성이라고 한다. 도한 속성과 메소드를 합쳐서 멤버라고 한다.

 

(2) 인스턴스 생성하기

클래스의 인스턴스 생성은 다음과 같이 시작한다. 클래스명 뒤에 () 인스턴스 생성에 필요한 값을 인수로 전달한다. 클래스정의에 인수가 없는 경우, 설명은 생략할 수 있다.

 

var 변수명 = 클래스명(인수명)

 

class Employee {

var emp_no: Int = 0

var name:String = “”

var title:String = “”

 

func getTitle() -> String {

return title

}

}

 

위 예제는 클래스를 정의하고 인스턴스를 생성하는 것을 보여준다. 클래스 정의에서 속성 또는 메소드가 포함되어 있지만 그것까지는 이해하지 않아도 된다. 그 부분에 대해서는 나중에 자세히 다루겠다.

클래스 정의는 키워드 class로 시작하고 { }안에 클래스 멤버(속성 및 메소드)를 구성한다. 작성한 클래스를 사용하려면 위 예제처럼변수와 상수에 대입되는 형태로 인스턴스가 생성되어야 한다. Employee 클래스는 인수를 받도록 설계되어 있지 않기 때문에 설명은 생략하고 인스턴스 생성 후 클래스가 가진 맴버를 사용할 수 있게 된다. 클래스 인스턴스가 있는 구성원을 조작하려면 인스턴명과 맴버를 마침표를 찍어 연결한다.

인스턴스명.속성명

인스턴스명.메소드명()

 

 

 

  1. 속성

클래스 맴버중 속성에 대해서 알아보자. 클래스에서 데이터를 관리하기 위해 속성을 사용한다. 스위프트언어의 속성에서는 유지형속성(Stored Properties)와 계산형속성(Computed Properties)가 있다.

(1) 유지형 속성(Stored Properties)

이미 알고 있는 변수와 상수선언은 클래스내에서 할 수 있지만, 이와 같은 클래스에서 선언된 변수나 상수를 유지형 속성이라고 한다. 주의할 점은 유지형 속성을 정의시 무조건 초기값을 할당해야 한다.

 

var 속성명:데이터형 = 초기값

let 속성명: 데이터형 = 초기값

 

class Employee {

var emp_no:Int = 0

var name:String = “”

var title:String = “”

}

 

유지형 속성은 var키워드로 선언하고 각 변수별로 빈상태로 초기화한다.

 

[lazy 속성]

일반적으로 클래스 소석은 클래스의 인스턴스가 생성된 시점에서 실체로 생성되지만 유지형속성에 lazy키워드를 사용하면 해당 속성이 처음 사용되었을 때 상태로 생성된다. 인스턴스 생성 자체에 시간이 걸리는데 실제로 사용되는 것이 아닌 경우에 유용하다. 단 let키워드로 선언된 유지형 속성에는 lazy속성을 사용할 수 없기 때문에 주의하자.

class ExamClass {

lazy var money:String = “1”

}

 

var examInstance = ExamClass()

 

print(examInstance.money)

 

[속성 모니터링]

속성에 새 값이 설정되었는지 여부는 willSet과 didSet으로 모니터링을 할 수 있다. willSet은 새 값이 설정되기 전에 가능하고 didSet은 새로운 값이 셋트되기 전에 처리된다.

var 속성명: 데이터형 {

willSet {

실행문

}

didSet {

실행문

}

}

 

willSet에서는 속성에 새로운 값을 newValue라고 선언해서 처리하고 didSet은 속성의 변경 전 값 oldValue라는 변수로 참조할 수 있다.

 

class Employee {

var emp_no: Int = 0

var name:String = “”

var title:String = “” {

willSet {

print(“직책을 (title)에사 (newValue)로 변경”)

}

didSet {

print(“직책을 (oldValue)에서 (title)로 변경”)

}

}

func getTitle() -> String {

return title

}

}

 

var emp = Employee()

emp.title = “과장”

 

마지막행의 title속성값을 대입하면 title값이 들어가기전에 willSet이 실행되고 newValue에 과장 설정값이 들어 있어 직책이 변경된다. Employee클래스의 title속성에 새 값을 할당하면 didSet이 실행된다

 

 (2) 계산형 속성 (Computed Properties)

계산형 속성은 유지형과 다르게 속성 자체는 값을 가지지 않고 클래스에 정의된 변수를 사용하여 계산한 값을 얻도록 한다. 이처럼 같은 값을 가져오기 위해 get, set블록이 있다.

var 속성명: 데이터형 {

get {

실행문

return 값

}

set (설정값) {

실행문

}

}

 

계산형 속성에 값을 할당할 때 그 속성의 set블록을 호출하여 할당된 값은 set인수로 전달된다. Set블록은 인수로 받은 값을 필요에 따라 가공하여 클래스에 정의된 변수로 설정한다. 반대로 계산형 속성의 값을 얻을 때 get블록을 호출한다. 그리고 get블록안에 return 키워드로 지정된 값이 계산된 속성값으로 리턴된다. 좀더 이해를 돕기 위해 예를 들어보자. 여행사가 취급하는 여행패키지가 있다고 가정해보고 TripPackage클래스를 정의한다. 이 클래스는 여행비용을 관리하는 price를 계산형 속성으로 정의한다. 계산형 속성이기 때문에 price자체는 값을 가지지 않고 _price에 값을 설정 및 얻는 형태로 데이터를 유지한다.

 

class TripPackage {

var _price: Double = 0.0

 

var price:Double {

get {

return _price * 1.08

}

set(newPrice) {

if newPrice<1000 {

_price = 1000

} else {

_price = newPrice

}

}

}

}

var hongkongTrip = TripPackage()

hongkongTrip.price = -1000

print(hongkongTrip.price)

 

클래스 정의후 여행패키지 클래스의 인스턴스를 생성하고 여행패키지 클래스의 price속성에 -1000을 대입한다. 따라서 set 블록의 인수 newPrice에 -1000이 들어간다. 여기서 여행 대금이 최소 1000원이상을 확보하고 싶기 때문에 1000미만인 경우 _price를 강제로 1000으로 설정하고 있다. 또한 newPrice값이 1000이상인 경우 그대로 _price를 대입한다. 마지막으로 현재 여행패키지 금액을 표시해준다. 이 때 계산된 속성의 get블록이 실행되기 때문에 _price값에 1.08을 곱한 부가세가 곱해진 금액이 return 된다.

이렇게 계산된 속성을 사용하면 잘못된 값이 설정된 것을 예방하고 필요에 따라 가공한 값을 리턴할 수 있다.

 

(3) 형 속성

클래스를 인스턴스화하지 않고 “클래스명.속성명”형태로 사용할 수 있는 속성을 유형속성(Type property)라고 한다. 형속성은 static으로 정의한다.

 

 statc var: 속성명 = 값

 

class Hello {

static var msg: String = “Hello”

}

print(Hello.msg)

 

Msg속성에 static키워드로 선언하고 이 속성의 형을지정한다.

 

 

3. 이니셜라이저

이니셜라이저는 속성의 초기화 처리와 인스턴스 생성시 수행할 작업을 기술해주고 사용하기 위한 메소드이다. 다른 프로그래밍 언어에서는 이를 보통 생성자라고 표현한다.  또한 클래스 내에 init가 선언되어 있지 않다면 자동으로 기본 이니셜라이저가 정의된다.

init(인수: 데이터형) {

실행문

}

 

class Hello {

var msg: String

init(msg: String) {

self.msg = msg

}

}

 

인수 msg에서 받은 문자열을 msg속성에 할당 초기화를 하고 있다. Self.msg의 self는 해당 클래스 자신을 나타낸다. 물론 self를 생략해도 되지만 인수처럼 클래스 자신이 가진 속성명과 동일한 경우 속성 msg인지 인수 msg인지 구별하기 위해 self를 선언해준다. 만약 앞에서 정의한 Hello 클래스의 인스턴스를 생성한다면 Hello 클래스 이니셜라이저는 인수를 취하기 때문에 msg: “Hello”형태로 인수를 전달한다. 따라서 Hello클래스의 msg속성에 “Hello”라고 설정된다.

var h: Hello = Hello(msg: “Hello”)

 

또한 이니셜 라이저는 함수이외에 오버로드를 할 수 있다. 예로 인자를 받는 경우와 받지 않는 경우를 구성해보겠다.

class Hello {

var msg : String

 

init() {

self.msg = “안녕하세요”

}

 

init(msg:String) {

self.msg = msg

}

}

 

var h1: Hello = Hello()

var h2: Hello = Hello(msg: “Hello World”)

 

그외 클래스에서 오버로더된 이니셜 라이저가 있다면, 이니셜 라이저에서 다른 이니셜 라이저를 호출할 수 있다.여기서 호출하는 이니셜라이저를 컨비니언스 이니셜 라이저(Convenience Initializer)라고 하고 호출된 이니셜라이저를 지정된 이니셜라이저라고 말한다. 컨비니언스 이니셜라이저를 정의할 때에는 init키워드앞에 convenience를 넣는다.

convenience init (인수:데이터형) {

실행문

}

 

class Hello {

var msg : String

 

convenience init() {

self.init(msg: “안녕하세요”)

}

 

init(msg:String) {

self.msg = msg

}

}

 

마지막으로 디이니셜라이저에 대해서 설명한다. 이는 인스턴스가 소멸될 때 자동으로 호출되는 메소드로 이를 호출하는 기술은 크게 필요하지 않는다. 다른 프로그래밍 언어에서는 보통 소멸자라고 표현한다.

일반적으로 메모리에 할당된 인스턴스 및 속성은 자동으로 삭제되기 때문에 이를 정의할 필요는 없다. 하지만, 파일을 다루는 클래스등을 만든다면 디이니셜라이저를 이용해서 안정적으로 파일을 닫아야 한다.

 

deinit {

인스턴스 삭제시 수행할 작업

}

 

class LifeStyle {

init(filepath: String) {

//여기에 파일관련 로직 추가

}

 

deinit {

// 인스턴스 리소스 해제

}

}

 

 

  1. 메소드

메소드는 클래스나 구조체, 열거형에 정의된 함수이다. 함수는 자신의 이름만을 지정하면 사용할 수 있는 방법은 “인스턴스.메소드”나 “클래스.메소드”처럼 인스턴스와 클래스와 같이 사용되는 점이 다르다. 메소드 정의방법은 기본적으로 함수와 동일하다. 그러나 메소드는 인스턴스 메소드와 형 메소드가 존재한다.

 

(1) 인스턴스 메소드

클래스를 인스턴스화한 후 인스턴스명과 같이 사용하는 방법을 인스턴스 메소드라고 한다.

class Hello {

func HelloMsg() {

print(“Hello”)

}

}

 

var h = Hello()

h.HelloMsg()

 

Hello클래스에 HelloMsg메소드가 하나 정의되어 있다. 함수정의 방법은 동일하다는 것을 기억하고 클래스인스턴스를 생성하고 “인스턴스명.메소드명”형태의 메소드를 사용한다. 이처럼 인스턴스를 생성하고 “인스턴스명.메소드()”형태로 사용하는 것을 인스턴스 메소드라고 말한다.

 

(2) 형 메소드

클래스를 인스턴스화하지 않고 “클래스명.메소드명”이라는 형태로 사용할 수 있는 메소드를 형 메소드(Type method)라고 한다. 형 메소든 func키워드 앞에 class를 정의한다.

class func 메소드명(인수목록) -> 리턴값의 데이터형 {

메소드에서 실행되는 문

return  리턴값

}

 

class Sum {

class func Snack(price:Int, num:Int) -> Int {

return price * num

}

class func WaterSet(price:Int, num:Int) -> Int {

return (price * num) * 6

}

}

 

Sum.Snack(1000, num: 5) // 5000

 

Sum.WaterSet(800, num: 6) // 28800

 

형 메소드의 예로 제품 가격합계를 요구하는 클래스 Sum을 정의하고 그 안에 과자합계를 구하는 Snack과 생수세트 합계를 구하는 WaterSet이라는 두가지 형 메소드를 정의하고 있다. 형 메소드는 인스턴스 생성없이 위 예제처럼 “클래스명.메소드명()”으로 사용한다.

 

 

  1. 액세스 한정자

클래스 정의에는 액세스 한정자라르 붙이는 것이 보통이다. 액세스 한정자는 정의된 클래스를 다른 파일에서 사용할 수 있도록 하거나 클래스에 정의된 메소드와 속성을 외부에 공개할 것인지를 결정할 때 사용한다.

 

  • Internal : 스위프트언어의 기본 액세스 권한으로 해당 액세스 한정자를 생략하면 기본 이것이 적용된다. 프로젝트의 다른 파일에서 접근이 가능하게 된다.
  • Private: 가장 제한적인 사용권한으로 동일 파일내에서만 접근이 가능하다.
  • Public : 어디에서든 사용할 수 있는 권한으로 다른파일이나 다른 모듈에서 접근이 가능하고 스위프트 언어에서 가장 광범위한 권한을 가진다.

 

(1) internal 수식

internal액세스 한정자는 다른 파일에서 사용하는 인스턴스를 생성할 수 있고 생성된 인스턴스를 통해 internal클래스내에 있는 internal 액세스 한정자를 가진 맴버를 사용할 수 있다.

 

internal class PersonInfo {

internal var name:String

 

init(name:String) {

self.name = name

}

 

internal func callHello() {

print(“(self.name)님 안녕하세요”)

}

}

 

class Hello {

let p = PersonInfo(name: “명준”)

 

init() {

print(p.name)

 

p.callHello()

}

}

 

PersonInfo 클래스에 정의된 클래스는 internal 액세스한정자로 선언된 name속성, callHello메소드, 이니셜라이저가 있다. Internal클래스이기 때문에 다른 파일에서도 이 클래스 인스턴스를 생성할 수 있다.

Hello클래스는 PersonInfo클래스의 인스턴스를 생성하는 것으로 생성된 인스턴스 p를 통해 PersonInfo클래스 name속성과 callHello메소드를 사용한다.

주의할 점은 internal클래스에서는 public이 사용되지 않기 때문에 속성과 메소드에 public을 사용하면 Xcode컴파일러는 경고를 표시한다.

 

(2) private 수식

private를 붙인 클래스는 동일한 파일에서 사용할 수 있지만 다른 파일에서는 사용할 수 없다. 또한 private클래스에 한정자를 생략하고 정의된 맴버는 자동으로 private되기 때문에 주의하자.

 

private class PersonInfo {

private var name:String

 

init(name:String) {

self.name = name

}

 

private func callHello() {

print(“(self.name)님 안녕하세요”)

}

}

 

internal class TeamProfile {

private let p = PersonInfo(name: “명준”)

 

}

 

PersonInfo클래스는 private한정자를 붙일 수 있지만 동일한 파일이면 접근이 가능하기 때문에  TeamProfile클래스에서 사용할 수 있다. 그러나 PersonInfo클래스의 private 맴버는 인스턴스 변수를 통해 접근이 가능하다.

 

(3) public 수식

internal한정자와 public한정자의 차이점은 모듈의 외부에서 접근할 수 있는지에 따라 달라진다. 모듈은 스위프트언어에서 사용하는 라이브러리를 말한다.

Internal한정자는 동일한 모듈내이라면 접근이 가능하다. 반대로 public모듈 테두리를 넘어 접근이 가능하며 가장 광범위한 액세스 한정자이다. 일반적인 용도의 클래스라면 internal한정자를 사용한다.

Facebook Comments

카테고리:   Swift

댓글

죄송하지만 댓글은 닫혀 있습니다.