0%

OOP기본 개념

OOP 기본

  • 객체: 클래스에서 생성된 변수, 클래스에서 파생되어 나오는 것, 혼자서 존재하는것은 의미가 없다. 그 객체와 다른 객체들간의 상호작용이 있을때 의미가 있다.

  • 객체지향 프로그래밍: 객체들을 설정하고 그 객체들간의 관계를 설계하는 것

    • 내가 필요한 객체가 뭔지를 설계 하는 것
    • 각 객체들간의 어떤형태로 상호작용을 하도록 만들것인지
    1
    "많은 개체들이 공통된 명칭을  가질 때 그것들은 언제나  또 하나의 이데아, 즉 형상을 갖는다. 가령 침대는 무수히 많지만 침대의 이데아, 즉 그 형상은 오직 하나이다. 여러가지 개개의 침대는 실재가 아니며 오직 그 이데아의 모사(模寫)에 의해 만들어졌을 뿐이다.”  - 플라톤
  • 똑같은 이데아 = 틀, 클래스

  • 이데아에 의해 만들어지게되는 실제 물건이나 형상 = 객체, 오브젝트

Class, Object

[ Class ]

  • 추상 (abstract) , 표현 대상에 대한 이데아(형상)

  • 이상적인 존재 (이미지, 설계도, 틀, 설명서)

  • 공통의 특징

  • 강아지라는 공통적인 개념(예시)

    [ Object ]

  • 실체(instance) , 추상을 실체화한 대상

  • 이데아의 모사

  • 개별 속성

  • 서로 다른 각각의 강아지들(예시)

Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let x = 5             // struct, Stack에 저장
let y = User() // class, Heap에 저장
let z = User() // class, Heap에 저장

x y z
[Stack] | 5 | 0x5F17 | 0x2C90 |
// Stack -> 처음부터 만들어지는 시점, 크기가 지정되어짐

0x2C90 0x5F16 0x5F17
[Heap] | z's user data | SomeData | y's user data |
// Heap -> 실제데이터는 힙에저장. 참조타입.

--- in Memory ---
값 타입(Value Type) - Stack
참조 타입(Reference Type) - Stack -> Heap

class <#ClassName>: <#SuperClassName>, <#ProtocolName...> {
// SuperClassName: 옵셔널, 상속받지않으면 생략가능
// ProtocolName: 상속받지않으면 생략가능
<#PropertyList>
<#MethodList>
}

let <#objectName> = <#ClassName()> // 변수에 클래스를 상속
objectName.<#propertyName>
objectName.<#functionName()>

Class Initialize(초기화 메서드)

  • 초기화(init)가 불필요한 경우 : 모든 저장 프로퍼티에 초기값이 설정되어 있음
  • 초기화(init)가 필요한 경우 : 저장 프로퍼티 중 하나 이상이 초기값 미설정됨
  • 모든 저장 프로퍼티 (Stored Properties)에 초기값이 설정된 경우 Init 메서드 작성 불필요
  • 객체 생성시 단순히 ClassName() 만으로 생성 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Dog {
let name: String
let color: String

// init메서드를 다양하게 지정해줄 수 있다.
init(name: String) {
// 컬러는 고정되어있는 init
self.name = name
color = "Brown"
}
init(name: String, color: String) {
// 두가지를 바꿀수있는 init
self.name = name
self.color = color
}

func bowwow() {
print("Dog Bowwow!")
}
}

Property

Stored Property(저장 프로퍼티)

  • Class ⭕️ Structure⭕️ Enum ❌
1
2
3
4
5
6
7
8
9
10
11
class StoredProperty {
var width = 0.0
var height = 0.0
}

let stored = StoredProperty()
stored.width = 123
stored.height = 456
stored.width // 123
stored.height // 456
// 저장된 값을 사용

Lazy Stored Property(지연 저장 프로퍼티)

  • 초기값이 인스턴스의 생성이 완료 될 때까지도 알 수 없는 외부 요인에 의존 할 때
  • 초기값이 복잡하거나 계산 비용이 많이 드는 설정을 필요로 할 때
  • 필요한 경우가 제한적일 때
  • Class ⭕️ Structure ⭕️ Enumeration ❌
  • 한번 저장된 값은 직접 변경해주지 않는 이상 바뀌지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class LazyStoredProperty {
var width = 10.0
var height = 20.0

// 1. 외부 요인이나 다른 설정에 기반
lazy var area = self.width * self.height
// lazy를 붙여주면 초기화가 끝나지 않아도 사용이 가능(area를 호출할때 비로소 만들어지기 때문)

// 2. 계산 비용이 많이 드는 상황
lazy var hardWork = "실행하면 약 10초 이상 걸려야 하는 작업"
// 화면을 띄우는순간 꼭 필요한 작업이 아닌데 lazy가아닌 일반 프로퍼티로 있으면 이작업을 실행후 화면을 띄울수 있기 때문에 사용자가 어떤 버튼을 눌렀을때만 필요한 작업이라면 미리할 필요가없기 때문에 이런상황에서 사용

// 3. 필요한 경우가 제한적인 상황(대부분 else로빠지는데 5% 상황만 true를 사용할때 메모리를 조금이라도 더아끼기 위해 초기화과정도 하지 않고 넘어가기 위해)
func ifStatement() {
if true { // 5%
print(area)
} else { // 95%
print(width)
}
}
}

Computed Property(연산&계산 프로퍼티)

  • 자료를 저장하지 않고 매번 호출할 때마다 새로 계산
  • Class ⭕️ Structure ⭕️ Enumeration ⭕️
1
2
3
4
5
6
7
8
9
10
// Computed Property 사용
var <#variablename>: <#type> {
get {
<#statements>
}
set {
<#variablename> = newValue
}
}
// 읽기전용 get, 쓸때는 set 도 붙임
  • get: 연산프로퍼티를 호출할때 보여지는 구간
  • Set: 연산프로퍼티에 값을 입력했을때 실행되는 구간 / 값을 저장할수 없으므로 저장프로퍼티를 이용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class ComputedProperty {
var width = 5.0
var height = 5.0

lazy var lazyArea = width * height
var area: Double { // 연산프로퍼티: 매번 새롭게 계산하기위해
return width * height // 읽기전용은 get생략 가능, 리턴도 생략 가능
}

// Stored + Computed get(read), set(write)
private var _koreanWon = 0.0 // 저장프로포티
var wonToDollar: Double {
get {
return _koreanWon / 1136.5
// 저장프로퍼티의 값을 이용해서 연산을 함
}
set {
_koreanWon = newValue
// set은 값을 저장할 수 없으므로, 저장프로퍼티를 이용한다.
// newValue 값을넣으면 여기로 들어옴. set에 새로들어오는 값을 newValue에 저장.
}
}
}

var computed = ComputedProperty()
computed.area // 25
computed.lazyArea // 25

computed.width = 10
computed.area // 50
computed.lazyArea // 위에서 최초 초기화를 시켰기에 그대로 25

computed.lazyArea = 50.0 // lazy는 호출된 이후엔 직접 값을 변경시켜줘야함
computed.lazyArea // 50

computed.width = 20
computed.height = 20
computed.area // 400
computed.lazyArea // 바꿔준 50으로 나옴

computed.wonToDollar
computed.wonToDollar = 10000
computed.wonToDollar // 만원을 입력시 환율된값이 나오도록 연산프로퍼티를 만듬

Property Observer(프로퍼티 옵저버)

  • Class ⭕️ Structure ⭕️ Enumeration ❌
  • newValue값이 들어오면 변경직전 및 직후 상태를 확인(반환)해 줄 수 있다.
1
2
3
4
5
6
7
8
9
10
11
// 저장프로퍼티 옵저버 사용
var <#variable> name: <#type> = <#value> {
willSet {
<#statements>
// 변수의 값이 바뀌기 직전 호출
}
didSet {
<#statments>
// 변경된 직후 호출
}
}
  • 추가적인 작업을 자동으로 반복해야 할때 그것을 별도로 또다시 만들지 않고 변수만 수정해도 자동으로 반영되도록
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PropertyObserver {
var height = 0.0
var width = 0.0 {
willSet {
print("willSet :", width, "->", newValue) // width = 0.0, newValue = 123.0
}
didSet {
print("didSet :", oldValue, "->", width) // old = 0.0 , width = 123.0
height = width / 2
}
}
}

var obs = PropertyObserver()
obs.height = 456
obs.height

obs.width = 123
obs.width
/*
willSet : 0.0 -> 123.0
didSet : 0.0 -> 123.0
*/

Type Property(타입 프로퍼티)

  • Shared(공유) / 하나의 타입이자 클래스가 모든것을 공유한다.
  • Lazily Initialized(지연 특성을 지님)
  • Class ⭕️ Structure ⭕️ Enumeration ⭕️
1
2
3
4
5
6
7
// 타입프로퍼티 사용
선언 - static let(var) <#propertyName>: <#Type>
class var <#propertyName>: <#Type> { return <#code> }
사용 - <#TypeName>.<#propertyName>

static: override 불가
class: 클래스에서만 사용 가능하고 computed property 형태로 사용. 서브클래스에서 override 가능
  • static var = 타입 프로포티, 객체를 만들지 않고 클래스에서 바로 .으로 사용가능
  • 어떤 도형을 만들고싶을때 높이를 똑같이 공유하고싶다. 이럴때도 사용이 가능
  • 타입전체가 공유하고자 할것을 만들 때
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class TypeProperty {
static var unit: String = "cm"

var width = 5.0
}

let square = TypeProperty() // 타입을 객체로 만들어서 프로포티에 접근
square.width

let square1 = TypeProperty()
square1.width = 10.0 // width의 값을 바꿔준다
square1.width

TypeProperty.unit
//타입자체에서 프로포티에 접근하는 방식 = 타입 프로포티 // = "cm"
print("\(square.width) \(TypeProperty.unit)") // 5.0 cm
print("\(square1.width) \(TypeProperty.unit)") // 10.0 cm

TypeProperty.unit = "m"
//단위를 전체적으로 바꾸고싶을때 unit자체를 바꿔준다. 모든 객체들이 공통으로 사용해야할 놈을 공유한다.
print("\(square.width) \(TypeProperty.unit)") // 5.0 m
print("\(square1.width) \(TypeProperty.unit)") // 10.0 m

Property 종류 정리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class MyClass {
// 저장 프로퍼티
var value1 = 0
// 지연 저장 : 호출했을 때 초기화
lazy var value2 = value1 + 10
// 연산
var value3: Int {
// 읽기
get {
let myValue = 50
return value1 + myValue
}
// 쓰기
set {
let myValue = newValue + 10
value1 = myValue + 20
}
}
// 프로퍼티 옵저버
var label = UILabel()
var text = "" {
willSet {
label.text = newValue
}
didSet {
label.text = text
}
}
var value4: Int = 100 {
willSet {
print("Value4가 \(value4)에서 \(newValue)으로 바뀔 것이다.")
}
didSet {
print("Value4가 \(oldValue)에서 \(value4)으로 바뀌었다.")
}
}
// 타입
static var value5: Int = 15
}
let myclass = MyClass()
myclass.value1 // 읽기
myclass.value1 = 10 // 쓰기
myclass.value2
myclass.value1 = 20
myclass.value2
myclass.value3 = 30
myclass.value1
myclass.value2
myclass.value3
myclass.value4 = 200
myclass.text = "집가고싶다"
MyClass.value5 = 25
MyClass.value5