상세 컨텐츠

본문 제목

Swift언어 복습노트 5 - 구조체와 클래스 그리고 instance(작성중)

Swift언어

by 앱등개발자IOS 2022. 9. 10. 17:29

본문

1. Swift의 구조체와 클래스 

 

 

일부 언어는 소스파일 하나에 구조체 or 클래스 1개만 선언 가능하지만,

Swift는 여러 개의 구조체, 클래스 정의하고 구현할 수 있다!

+ 구조체 안의 구조체 / 클래스 안의 클래스  중첩 타입 정의 가능!!

 

- 구조체 let으로 선언하면, 내부 프로퍼티 값 변경할 수 없다! (var로 선언 시 가능 )

- 클래스의 "인스턴스"는 "참조타입"이므로 let(상수)으로 선언해도 내부 프로퍼티 값 변경 가능!!!!

# Swift 클래스 인스턴스 (Swift에서 유일한 참조 타입)

- 클래스의 Instance는 참조타입이므로, 참조할 필요가 없을 때 메모리에서 해제된다!

- 메모리 해제 직전deinit(소멸자)이 호출된다 ( deinit은 클래스 당 하나 구현 가능)

- 인스턴스 소멸 전에 데이터 저장, 소멸을 다른 객체에 알리는 등의 코드를 deinit{} 내부에 작성

# Swift 구조체 & 클래스 공통점 & 차이점

공통점 

- 프로퍼티 & 메서드 정의, 접근 가능

- 초기화를 위해 Initializer 정의 가능

- extension으로 확장 가능

- protocol 준수 가능

차이점 

- 구조체는 상속 불가

- 클래스의 Instance만  DeInitializer 활용 가능

- 클래스 Instance만 Reference Counting 함 ( 클래스 인스턴스만 참조타입이므로 )

#구조체, 클래스의 차이

- 구조체는 상속 불가

- 타입캐스팅은 클래스의 인스턴스만 가능

- deinit은 클래스의 인스턴스

- Reference counting은 클래스 인스턴스만 

# 값 타입 & 참조 타입

함수의 전달인자로 전달 되는 경우 & 새로운 변수에 할당될 때  모두!

- 값 타입 : 값이 복사되어 전달

- 참조 타입 : 값을 복사X, 주소(참조)를 전달!!

 

부연설명 :

* 함수의 전달인자로 값 타입 데이터 전달? => 메모리전달인자를 위한 인스턴스가 새로 생성

* 함수 전달인자로 참조 타입 전달? => 주소만 전달!

### Swift의 Bool, Int, Array, Dictionary, Set 등 모두 struct로 구현된 값타입!!!!!!!!

### 언제 클래스 말고 구조체를 사용하나?  ( Apple 가이드라인 )

1. 캡슐화만이 목적일 때

2. 상속을 할 필요가 없을 때

3. 프로퍼티를 참조하는 것보다 복사하는 것이 합당할 때!

 

 

2. 프로퍼티와 메서드

 

# Swift 프로퍼티

- 프로퍼티는 저장 프로퍼티(Stored Properties), 연산 프로퍼티(Computed Properties), 타입 프로퍼티(Type Properties)

- 프로퍼티 감시자(Property Observers)는 프로퍼티 값 변화를 관찰하여 특정 작업을 실행함 ( 저장 프로퍼티에만 적용 가능 )

 

#1. 저장 프로퍼티

- 가장 단순한 개념의 프로퍼티

- 구조체든 클래스든, Instance를 만들 때, 저장 프로퍼티에 값이 꼭 있어야한다!( 옵셔널 저장 프로퍼티 제외 )

- 구조체는 저장 프로퍼티 기본값을 지정하지 않아도, 알아서 해당 프로퍼티들 채우는 생성자 제공.

- 클래스옵셔널이 아닌 모든 프로퍼티들을 초기화할 수 있는 생성자를 사용자 정의 해주어야한다.

 

#2. 지연 저장 프로퍼티 ( 키워드 : lazy var )

- 상수는 인스턴스 생성되기 전에 초기화 해야하므로 지연저장 불가

- 굳이 모든 저장 프로퍼티를 모든 인스턴스가 사용하지 않아도 될텐데, 지연저장 프로퍼티를 사용하면, 불필요한 성능 저하, 공간낭비 줄일 수 있다.

 

#3. 연산 프로퍼

- 인스턴스 내/외부 값을 연산하여 적절한 값 돌려주는 Getter

- 은닉화 된 내부 값 간접적으로 설정하는 Setter

- 메서드로 구현해도 되는 것이지만,

        1. 메서드로 구현할 경우 메서드 두 개 구현해야함.

        2. 두 메서드가 분산되어 코드 가독성 나빠질 위험

        3. 연산프로퍼티가 더 간편,직관적이다!

 

- But, 읽기 전용(Getter) 상태로 구현하기 쉽지만, 쓰기 전용 상태로는 구현할 수 없다!!!

 

예시)

같은 기능을 메서드가 아닌, 연산 프로퍼티로 구현한 예시

-> 하나의 프로퍼티에 대한 Getter, Setter가 모여있어 명확하고 직관적임을 알 수 있다.

 

#4.  프로퍼티  감시자(Property Observer)

- 프로퍼티 값이 새로 할당될 때마다 호출

- willSet => 프로퍼티 값이 변경되기 직전에 호출 ( 변경될 값을 매개변수로 줌 newValue)

- didSet =>  프로퍼티 값이 변경된 직후에 호출 ( 변경되기 전 값을 매개변수로 줌 oldValue)

- 전역변수, 지역변수에도 Property Observer 달아줄 수 있다!

-> 일반 저장 프로퍼티 / 상속받은 연산프로퍼티 재정의 에서만 Property Observer 사용 가능!!

==> why? 연산 프로퍼티는 감시자를 사용할 필요도 없고 할 수도 없는 이유는, 연산프로퍼티 내부에서 값의 변화에 대한 구현이 다 가능하기 때문.

 

#5. 타입 프로퍼티 (Type Property)

- 저장 프로퍼티, 지연저장 프로퍼티, 연산 프로퍼티와 같이 인스턴스가 생성될 때마다 생성되는 "인스턴스 프로퍼티"가 아닌,

Type 자체에 속하는 프로퍼티를 "타입 프로퍼티"라고 함

 

-인스턴스 생성과 상관 없이 타입 프로퍼티 값은 하나

- 해당 타입 모든 인스턴스가 공통으로 사용하는 값

- 모든 인스턴스에서 공용으로 접근, 값 변경할 수 있는 변수

 

# 6. 메서드

* Swift에서는 클래스, 구조체, 열거형이 실행하는 기능을 캡슐화하여 인스턴스 메서드를 정의할 수 있다.

* 타입 자체와 관련된 기능을 하는 타입 메서드 ( 기존 언어에서의 클래스 메서드와 유사한 개념) 정의할 수 있다.

 

-## 구조체, 열거형메서드를 가진다는 것이 기존 언어와 Swift의 큰 차이!!!

 

- 인스턴스 메서드 :

    특정 타입의 인스턴스에 속한 함수를 뜻함. => 기존 언어들에서의 클래스 내의 함수라고 생각하면 됨. 

    인스턴스 내부의 프로퍼티 값을 변경하거나 특정 연산 결과를 반환하는 등 인스턴스와 관련된 기능을 실행함.

   

But! 구조체나 열거형 값 타입에서 '해당 메서드가 인스턴스 내부 값을 변경할 것'이라는 명시 해야함. (mutating)

위와 같은 컴파일 오류가 뜬다.

 

또한, let 으로 구조체 인스턴스를 생성할 경우, 내부 프로퍼티 값을 변경할 수 없다.

 

# 7. Self 프로퍼티

* 모든 인스턴스는 암시적으로 생성된 self 프로퍼티를 가짐 (자바의 this와 비슷)

* self는 다른 언어에서와 같이 인스턴스(본인)을 명확히 지칭하고자 할 때 사용함

## 같은 이름을 사용했을 때 Swift는 지역변수 -> 메서드 매개변수 -> 인스턴스 프로퍼티 순으로 추측!

 

아래 그림18번 줄처럼 self로 프로퍼티 level 을 명확히 지칭할 수 있다.

 

## self프로퍼티의 다른 용도!

=>  인스턴스 자체를 바꿔 끼울 수 있다!!  ( 클래스 Instance는 참조타입이므로 self프로퍼티에 다른 참조 할당 불가. but 구조체, 열거형 등 값 타입 인스턴스는 self 프로퍼티를 사용하여 자신 자체를 치환 가능)

 

## 타입 메서드

=>  메서드 앞에 static 키워드를 사용하여 "타입 메서드"임을 나타냄

=> 클래스의 경우, static, class 두가지로 타입 메서드 명시.

==> static : 상속 후 override 불가 / class : 상속 후 override 가능 

 

3. 인스턴스 생성 및 소멸

인스턴스는 사용 후 소멸 시점이 오면 소멸한다.  클래스 인스턴스의 소멸은 ARC와 관련됐고, 이니셜라이저는 상속된다.

# Swift 이니셜라이저(init)

- Swift의 init로 초기화 과정 구현 가능, 반환값은 없다.

- class, struct, enum 모두 func 키워드 없이 init(){ ...} 형태로 이니셜라이저 작성 

- init ()도 매개변수를 가질 수 있고 여러개의 init() 정의 가능! 

- 사용자정의 이니셜라이저를 하나라도 만들면, 기본 init()과 멤버와이즈 init (구조체 특권)을 사용할 수 없다.

 

# Swift 프로퍼티 기본값

- 초기화 후에 값이 확정되지 않은 저장 프로퍼티는 존재할 수 없다!

- 이니셜라이저를 통해 초깃값 할당 or 프로퍼티 기본값을 통해 저장 프로퍼티가 초기화될 때는 Property Observer 호출되지 않음

 

# Swift 옵셔널 프로퍼티 (var age: Int?)

- 초기화 과정에 값을 초기화해주지 않아도 됨.

- 옵셔널 property는, 초기화 과정에 값을 할당해주지 않는다면 자동으로 nil이 할당 됨 

 

# Swift 상수 프로퍼티

- 상수 저장 프로퍼티는 인스턴스 초기화 과정에서만 값 할당 가능, 이후 값 변경 불가

- 자식클래스에서 부모클래스 상수 프로퍼티 값 초기화 불가능

 

# Swift 초기화 위임

- 사용자 정의 이니셜라이저를 정의하면, 기본 init()과 멤버와이즈 init() 사용 불가능하므로, 초기화 위임 ( 정의한 init()을 다른 init()안에서 사용 -> 중복되는 코드 줄임)하려면 최소 2개의 init()을 정의해줘야한다.

# Swift 실패 가능한 initializer

- init 대신 init?(){ ...} 키워드 사용. => 실패한 경우 nil 반환 

 

# Swift 함수 or 클로저 사용한 프로퍼티 기본값 설정

- class ~~ {

    let a: SomeType = { 

            ~~~연산 

    }()

}

위와 같이   프로퍼티  초기화에 클로저나 함수를 사용하면, 실행 시점은 인스턴스의 다른 프로퍼티 값이 설정되기 전이다!

즉, 클로저 내부에서 다른 프로퍼티 사용하여 연산 불가능!!

## 클로저 뒤 ()는 해당 클로저를 실행하기 위한 표현. ()가 없다면 클로저 그 자체가 할당되게 된다.

# Swift Deinit()

- Class 인스턴스에만 구현 가능! (참조 타입이니까)

- deinit 키워드 사용

- 메모리에서 해제되기 직전 원하는 작업 수행 가능!

- init()과 다르게 매개변수 갖지 않음

 

Swift는 인스턴스 더 필요하지 않으면 자동으로 메모리에서 소멸시킴 (ARC)

## 인스턴스 내부에서 파일을 열었었다면 다시 저장하고 닫는 등의 작업, 데이터를 저장하는 작업에 사용 

 

 

4. 접근 제어

# Swift 접근 수준(Access Level)

* open - public - Internal - fileprivate - private 순이다.

# open - 개방 접근수준 & 모듈 외부까지 (클래스에서만 사용)

# public - 공개 접근수준 & 모듈 외부까지

# Internal -  내부 접근수준 & 모듈 내부

# fileprivate - 파일외부비공개 접근수준 & 파일 내부

# private - 비공개 접근수준 & 기능 정의 내부

 

 

관련글 더보기