Swift의 객체지향 프로그래밍
객체란 무엇인가?
객체: 소프트웨어 애플리케이션을 구축하는 블록으로, 쉽게 사용하고 재사용할 수 있는 독립적인 기능 모듈
- 속성(property, 프로퍼티)
- 함수(method, 메서드)
클래스 멤버(class member)
클래스란 무엇인가?
객체가 생성될 때의 모습을 정의
ex) 메서드들이 하게 될 일, 어떤 프로퍼티들이 존재할지..
클래스 선언하기
class 새로운 클래스 이름: 부모 클래스 {
// 프로퍼티
// 인스턴스 메서드
// 타입 메서드
}
- 어떤 부모 클래스(parent class)에서 파생되었는지
- 프로퍼티(property): 클래스 내에 포함될 변수와 상수 정의
- 인스턴스 메서드(instance method), 타입 메서드(type method): 클래스에서 호출되는 메서드들과 클래스의 인스턴스들을 정의
class BankAccount {
}
클래스 이름을 지을 때, 각 단어의 첫 번째 문자를 대문자
프로퍼티 이름과 함수의 이름은 첫 번째 문자를 소문자
클래스에 인스턴스 프로퍼티 추가하기
- 객체지향 프로그래밍의 핵심 목적 - 데이터 캡슐화(data encqpsulation)
- 클래스에 저장되고 접근될 수 있는 데이터는 오직 해당 클래스 내에 정의된 메서드만을 통함
프로퍼티(property) or 인스턴스 변수(instance variable): 클래스 내의 캡슐화된 데이터
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
}
메서드 정의하기
- 타입 메서드(type method): 클래스 레벨에서 동작
ex)새로운 인스턴스 생성 - 인스턴스 메서드(instance method): 클래스의 인스턴스에 대한 작업
ex) 두 개의 프로퍼티 변수에 대한 산술 연산을 하고 결과를 반환class BankAccount { var accountBalance: Float = 0 var accountNumber: Int = 0 func displayBalance() { // 인스턴스 메서드 print("Number \(accountNumber)") print("Current balance is \(accountBalance)") } class func getMaxBalance() -> Float { // 타입 메서드 return 100000.00 } }
타입 메서드는 선언부 앞에 class 키워드가 붙는다
클래스 인스턴스 선언하기와 초기화하기
클래스를 가지고 작업을 하기 위해 -> 클래스의 인스턴스를 생성
-> 인스턴스에 대한 참조체를 저장할 변수 선언
var account1: BankAccount = BankAccount()
클래스 인스턴스 초기화하기와 소멸하기
초기화작업(init 메서드)
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
init(number: Int, balance: Float) {
accountNumber = number
accountBalance = balance
}
func displayBalance() { // 인스턴스 메서드
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
}
}
var account1: BankAccount = BankAccount(number: 12312312, balance: 400.54)
정리작업(deinit 메서드): 클래스 인스턴스가 없어지기 전에
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
init(number: Int, balance: Float) {
accountNumber = number
accountBalance = balance
}
deinit {
// 필요한 정리 작업 수행
}
func displayBalance() { // 인스턴스 메서드
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
}
}
메서드 호출하기와 프로퍼티 접근하기
인스턴스 메서드 호출법, 프로퍼티 접근법 => 점 표기법(dot notation)
클래스인스턴스.프로퍼티명
클래스인스턴스.인스턴스메서드()
var balance1 = account1.accountBalance
accountBalance 인스턴스 변수의 현재 값을 balance1 변수에 저장
account1.accountBalance = 6789.98
accountBalance 인스턴스 프로퍼티에 값 설정
account1.displayBalance()
클래스 인스턴스의 displayBalance 메서드 호출
타입 메서드는 클래스 인스턴스가 아닌 클래스에서 호출
클래스이름.타입메서드()
var maxAllowed = BankAccount.getMaxBalance()
저장 프로퍼티와 연산 프로퍼티
저장 프로퍼티(stored property): 상수나 변수에 담기는 값
연산 프로퍼티(computed property): 프로퍼티에 값을 설정하거나 가져오는 시점에서 어떤 계산이나 로직에 따라 처리된 값, getter, setter(선택) 생성
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
let fees: Float = 25.00
var balanceLessFees: Float {
get { // getter 메서드
return accountBalance - fees
}
}
init(number: Int, balance: Float) {
accountNumber = number
accountBalance = balance
}
.
.
.
}
balanceLessFees 프로퍼티는 accountBalanace에서 fees를 뺀 값이 저장
var balanceLessFees: Float {
get {
return accountBalance - fees
}
set(newBalance) { // setter 메서드(선택사항)
accountBalance = newBalance - fees
}
}
balanceLessFees 프로퍼티에 값이 설정될 때 새로 설정된 newBalance의 값에서 fees를 뺀 값이 accountBalance에 저장
var balance2 = account1.balanceLessFees //getter메서드 호출, return 6789.98 - 25 = 6764.98
account1.balanceLessFees = 12123.12 // setter메서드 호출, newBalance = 12123.12, accountBalance = 12123.12 - 25 = 12098.12
지연 저장 프로퍼티
프로퍼티 초기화 방법
- 직접 할당
var myProperty = 10
- 초기화 작업에서 프로퍼티에 값을 할당
class MyClass { let title: String init(title: String) { self.title = title } }
- 클로저 이용
class MyClass { var myProperty: String = { var result = resourceIntensiveTask() result = processData(data: result) return result }() . . }
클로저를 이용하여 선언하면 해당 프로퍼티가 코드 내에서 실제로 사용되는지와는 상관없이 클래스의 인스턴스가 생성될 때마다 초기화 작업이 수행
-> 계속 초기화되면 후반부에 프로퍼티 값이 할당되었는지 모르게 되는 상황
-> 프로퍼티를 최초로 접근할 때만 초기화 작업 수행
-> 프로퍼티를 lazy로 선언class MyClass { lazy var myProperty: String = { var result = resourceIntensiveTask() result = processData(data: result) return result }() . . }
지연 프로퍼티는 반드시 변수(var)로 선언!
self 사용하기
class MyClass {
var myNumber = 1
func addTen() {
self.myNumber += 10
}
}
self는 MyClass 클래스 인스턴스에 속한 myNumber라는 이름의 프로퍼티를 참조
스위프트에서 self는 선택적으로 사용 (BECAUSE self는 프로퍼티와 메서드에 대한 참조를 디폴트로 간주)
func addTen() {
myNumber += 10
}
<self를 사용해야 하는 상황>
- 클로저 표현식 내에서 참조할 경우
document?.openWithCompletionHandler({(success: Bool) -> Void in if success { self.ubiquityURL = resultURL } })
- 동일한 이름을 가질 경우
class MyClass { var myNumber = 10 // 클래스 프로퍼티 func addTen(myNumber: Int) { print(myNumber) // 함수의 매개변수 값을 출력 print(self.myNumber) // 클래스 프로퍼티 값을 출력, 10 } }
프로토콜 이해하기
프로토콜(protocol): 클래스가 충족해야 하는 최소한의 요구사항을 정의하는 규칙들의 집합
- protocol 키워드를 이용하여 선언
- 클래스가 반드시 포함해야 하는 메서드와 프로퍼티를 정의
protocol MessageBuilder {
var name: String { get }
func buildMessage() -> String
}
이 프로토콜을 채택하는 클래스는 name 프로퍼티와 buildMessage 메서드를 반드시 포함
class MyClass: MessageBuilder {
var name: String
init(name: String) {
self.name = name
}
func buildMessage() -> String {
"Hello" + name
}
}
불투명 반환 타입
함수가 결과를 반환한다면 함수 선언부에 결과의 타입이 포함
func doubleFunc1(value: Int) -> Int {
return value * 2
}
정수형 결과를 반환(특정 반환 타입( 구체화된 타입(concrete type)))
불투명 반환 타입(opaque return type): 모든 타입이 반환
- some 키워드를 붙여 선언
func doubleFunc1(value: Int) -> some Equatable {
return value * 2 // Int
}
func doubleFunc2(value: String) -> some Equatable {
return value + value // String
}
let intOne = doubleFunc1(value: 10) // Int
let stringOne = doubleFunc2(value: "Hello") // Float
if (intOne == stringOne) { // 오류 발생
print("They match")
}