SeSAC iOS 데뷔과정 2기

0818 새싹 33회차 정리 - TypeCasting, Generic

Lia's iOS 2022. 8. 18. 23:03
iOS 앱 개발자 데뷔 과정 33회차

 

 

 

 

#1. Checklist

- TypeCasting

- Type Cast operator

인스턴스 타입을 확인하거나, 자신의 인스턴스 타입을 다른 타입의 인스턴스인 것처럼 사용할 때 활용되는 개념이다.

타입 캐스팅을 하더라도 실제 인스턴스 타입은 메모리에 남아있다.

is 는 어떤 클래스의 인스턴스 타입인지, 어떤 데이터 타입인지 확인할 때 사용하며 Bool을 반환한다.

 

- upcasting : as

컴파일러가 캐스팅에 대한 성공을 확신할 수 있는 경우 as 를 사용할 수 있다.

일반적으로 부모 클래스의 타입인 것을 알고 있을 때 as 를 사용한다.

 

- downcasting : as! as?

부모 클래스 타입을 자식 클래스 타입으로 내려서 캐스팅하는 것을 말한다.

타입이 부모 클래스일 경우, 자식 클래스의 멤버에 접근할 수 없기 때문에 다운캐스팅이 필요하다.

as? 는 옵셔널 반환 타입을 반환하고, 다운캐스팅이 실패할 경우 nil을 반환한다.

as! 는 논옵셔널 타입을 반환하기 때문에, 실패할 경우 런타임 오류가 발생한다.

 

 

 

- Any vs AnyObject

AnyObject : https://developer.apple.com/documentation/swift/anyobject 참고

Any와 AnyObject 모두 여러 타입에 대응할 수 있지만 Any는 말 그대로 모든 타입에 대한 인스턴스를 담을 수 있고, AnyObject는 모든 클래스 타입의 인스턴스만 담을 수 있다.

둘 다 어떤 타입으로 된 요소를 가지고 있는지 컴파일 시점에서는 알 수 없고, 런타임 시점에 타입이 결정되기 때문에 타입 캐스팅을 하지 않으면 각 멤버에 대한 접근이 불가능하다.

런타임 시점에 결정되기 때문에 런타임 오류가 발생할 수 있어 사용시 주의가 필요하다.

 

 

 

- Generic

타입에 유연하게 대응하기 위해 사용한다.

제네릭으로 구현한 타입과 기능은 재사용이 용이하고, 코드의 중복을 줄일 수 있기 때문에 깔끔한 표현이 가능하다.

(참고) 함수를 오버로딩하여 Generic을 사용할 경우, Generic으로 설정한 함수보다 타입이 명확한 함수가 우선순위가 높다.

(참고) protocol에서 Generic을 사용하고자 할 경우, associated type을 사용해야 한다.

 

- (참고) inout Parameters

본래 파라미터로 전달받은 값은 함수 내에서 변경이 불가하다.

만약 전달받은 a와 b라는 파라미터의 값을 서로 바꾸고 싶다면, inout 이라는 키워드를 타입 앞에 붙여야 한다.

참고로 값을 서로 바꾸는 swap이라는 함수는 이미 스위프트에서 구현이 되어있으며, 학습 차원에서 직접 구현했다.

 

- Type Parameters

제네릭을 사용할 때는 일반적으로 T, U, K 를 사용하며, 이는 Type Parameters로 플레이스 홀더와 같은 역할을 한다.

타입의 종류는 알려주지 않지만, 제네릭으로 이루어진 함수를 사용할 때에 T에 들어갈 타입은 모두 같아야 한다.

T가 아닌 다른 문자열을 작성하는 것도 가능하며, UpperCased로 작성한다.

 

- Type Constraints

여러 타입에 대응할 수 있는 점은 같지만 Any와의 차이점은, 제네릭은 특정 타입만 받을 수 있도록 제약조건을 줄 수 있다는 것이다.

 

 

 

위와 같이 프로토콜 제약을 설정하면 해당 프로토콜을 채택하지 않는 String 등은 제네릭 함수를 사용할 수 없다.

Numberic 프로토콜을 채택하는 타입만이 제네릭 함수를 사용할 수 있기 때문에 reduce로 연산이 가능해진다.

 

프로토콜 제약과 비슷하게, 클래스 제약을 설정하면 해당 클래스를 상속받지 않는 타입은 사용이 불가능하다.

 

 

 

- Pass data between view controllers - Closure

VC간의 데이터 전달을 2번에서 1번 컨트롤러로, 반대로 넘기는 방법에 대해 클로저를 활용해서 구현해보자

코드로 UI를 구성했기 때문에 viewDidLoad에서 addTarget을 통해 버튼에 액션을 추가한다.

그리고 action에 넣을, 실질적인 동작을 담당할 함수를 @objc로 작성한다.

 

2번 VC - ProfileViewController

액션에 대한 핸들러를 받을 변수를 옵셔널로 선언하고, saveButton을 탭했을 때 클로저를 실행한다.

그럼 이 핸들러에 어떤 클로저를 넣을지, 실질적으로 saveButton이 어떤 동작을 할지는 1번 VC에서 정할 수 있다.

 

1번 VC - ViewController

nameButton을 탭했을 때 2번 VC로 이동이 되기 때문에 메모리와 코드의 가독성의 측면에서 핸들러는 이곳에 작성했다.

2번 VC의 인스턴스를 만들고, 텍스트필드의 텍스트를 가져와 자신의 타이틀을 변경하는 클로저를 핸들러로 전달한다.

이렇게 하면 nameButton을 탭했을 때 ViewController (1번 VC) 가 클로저를 전달하고, 클로저를 전달받은 핸들러는 가만히 있다가 saveButton을 탭했을 때 작동하며 ProfileViewController (2번 VC) 의 데이터를 전달하게 된다.

정리하고 보니 값을 전달한다기 보다는, 2번 VC에서 대문을 열어놓으면 1번 VC가 멋대로 꺼내가는 느낌이 드는 것 같다...

 

 

 

 

#2. Assignment

- Type Casting

swift.org에서 Type Casting에 대한 예제 실습 : https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342

 

 

 

- 클로저를 활용한 값 전달

오늘 배웠던 내용처럼 값 전달을 반대로 하고 싶었는데, 마땅한 예시가 떠오르지 않아 정방향으로 보냈다.

사실 이럴거면 굳이 클로저를 사용하지 않아도 되지만...

 

 

 

 

# 그 외

- Equatable 프로토콜과 등위연산자

아래의 예시에서 one == two 를 비교하려면 name 프로퍼티까지 접근해야 한다.

그냥 클래스의 인스턴스끼리 비교하면 어느 멤버를 기준으로 판단해야 하는지 모르기 때문이다.

 

등위연산자를 사용하려면 Equatable 프토토콜을 준수하고 있어야 한다.

그래서 Animal Class에서 Equatable를 채택하면 필수로 구현해야하는 요구사항에 대한 에러메세지가 뜬다.

요구사항을 만족하도록 코드를 작성하고 나면, one == two 라는 연산이 가능하게 된다.

 

 

 

- extension과 Generic을 활용한 화면 전환

스토리보드로 UI를 구성했을 때, extension을 통해 메서드를 만들어 놓으면 화면전환을 조금 더 편하게 구현할 수 있다.