iOSInterviewquestions - Swift
목적 : 배울때는 잘했는데, 나이가 먹으니까 자꾸 까먹어서 아래 깃허브 글을 바탕으로 꾸준히 이론 공부를 하고자 함. 물론 설명은 내가 잘 아는게 목적이라 철저히 나를 이해시킬 수 있는 나의 언어로 작성.
작성일 : 2022년 4월 2일
- README: 추후에 수정이 있을 경우 아래에 날짜와 시각을 작성하고, 수정 내용을 간략히 정리해주세요.
예시)
(수정) oooo년 oo월 oo일 oo시 oo분
- 변경내용 : xxx에 대한 자료를 보강
✅ 공부의 기준이 되는 깃허브 글
https://github.com/JeaSungLEE/iOSInterviewquestions
✅ Swift
- struct와 class와 enum의 차이를 설명하시오.
- class : 상속이 가능하며, call by ref 데이터 전달, 메모리의 위치를 전달한다. 참조하는 값이 존재하는 위치를 stack 메모리 영역에 참조되는 값은 heap 메모리 영역에 저장.
- struct : 상속이 불가능하며, call by value로 데이터 전달, 값이 복사되어 새로운 인스턴스가 생성되고 이 인스턴스가 stack 영역에 저장
- enum : 다른 언어의 enum과는 조금은 다르며, 개인적으로는 기능이 매우 우수함. enum자체가 하나의 데이터 타입이며, 기존 프로퍼티는 값을 저장하는 역할을 하나, 연산 프로퍼티는 특정한 연산을 통해 값을 리턴해줌
- class의 성능을 향상 시킬수 있는 방법들을 나열해보시오.
- 스위프트는 자동으로 메모리 할당과 해제를 처리한다. 메모리 할당과 해제는 Stack 또는 Heap에서 처리한다.
- Stack은 LIFO의 단순한 구조로 메모리 할당과 해제가 편리하다. 시간복잡도는 O(1)로 속도가 매우 빠르며 Stack Pointer로 사용하여 할당과 해제를 처리한다.
- Heap은 Stack보다 좀 더 복잡하다. 다이나믹한 할당 방법을 사용하는데, Heap영역에서 사용하지 않는 블록을 찾아 메모리 할당을 처리한다. 할당을 해제하기 위해, 해당 메모리를 적절한 위치로 다시 삽입한다. 여러 스레드가 동시에 Heap에 접근한 수 있기에 Locking 또는 다른 동기화 매커니즘으로 무결성을 보호해야 한다.
- 레퍼런스 카운팅인데 Struct에서도 레퍼런스 카운팅이 일어난다. String타입을 갖고 있으면 이는 힙 영역에 저장되며, UIFont도 클래스로 만들어진 객체이므로 레퍼런스 카운트가 필요하다.
- 이를 해결할 방법으로는 String타입이 아닌 struct타입의 새로운 타입을 만들던가, enum을 사용하여 해결할 수도 있다.
- Method Dispatch는 프로그램이 어떤 메소드를 호출할 것인지 결정하여 그 메소드를 호출하는 과정을 뜻함. 어떤 메소드인지 결정되는 시점에 따라 static과 dynamic으로 나뉨.
- static method dispatch는 컴파일 시점에 컴파일러가 메소드의 실제 코드 위치를 파악할 수 있어 런타임에 찾는 과정 없이 바로 코드를 실행한다. 구현된 코드들이 어디서 실행되는지 알 수 있기에 메소드 인라이닝과 같은 코드 최적화를 실행한다. 메소드 인라이닝이란 메소드를 호출할 때, 해당 메소드로 이동하지 않고, 메소드의 결과값을 바로 반환하여 성능을 향상시킨다.
- dynamic method dispatch는 컴파일 시점에 어떤 메소드를 호출하는지 알 수 없어서, 런타임에 table을 참조하여, 해당 메소드에 대한 정보를 가져와서 코드를 실행한다. static 방식보다 많은 비용을 필요로 하지 ㅇ낳고, 레퍼런스 카운팅, 힙할당과 같은 쓰레드 동기 오버헤드가 없다. 하지만 컴파일러는 static은 최적화 작업이 가능하지만 dynamic은 추론할 수 없다. 그렇지만 필요한 이유가 있는데, 바로 다형성 때문이다. 다형성이란 하나의 객체가 여러 타입을 가질 수 있는 것을 의미한다.
-
class Drawable { func draw() {} } class Point: Drawable { var x, y: Double override func draw() { ... } } class Line: Drawable { var x1, y1, x2, y2: Double override func draw() { ... } } var drawables: [Drawable] for d in drawables { d.draw() }
- 위의 코드에서 보면 Point와 Line 클래스가 Drawable를 상속하고 있다. 이 상황에서 d.draw의 d가 Point인지 Line인지 알기 힘들다. 이를 해결하기 위해서는 컴파일러는 클래스에 타입 정보에 대한 포인터를 저장하는 virtual method table을 추가한다. static memory에 저장하며, 이 테이블을 통해 메소드를 호출한다.
- final class : 서브 클래스를 만들지 않는다면 final을 선언한다. 그러면 컴파일러가 static하게 dispatch할 수 있다. 또한 서브클래스를 만들지 않는다는 의도도 보여줄 수 있다.
- 파일 내에서만 접근해도 될 경우 private선언
- private로 선언할 경우 참조 가능한 범위가 현재 파일로 제한되며 이에 컴파일러는 해당 프로퍼티가 참조될 수 있는 구역 내에서 오버라이딩 될지, 안될지를 알아서 판단할 수 있게 된다. 만약 오버라이딩되는 곳이 없다는게 판단될 경우 컴파일러가 스스로 final로 추론해서 static dispatch로 동작시킨다.
- WMO(whole module optimization) 사용하기.
- 모듈 전체를 하나의 덩어리로 컴파일하기 때문에 internal level에 대해서 오버라이딩이 되는지, 아닌지를 추론 가능하고, 오버라이딩이 없을 경우 final을 선언해주는 것이 whole module이며 Xcode의 compilation Mode의 release에서 설정 가능하다.(자동으로 whole module이 켜져 있다.)
- 컴파일을 파일 단위로 하게 되면 현재 파일의 class가 상속이 없음에도 불구하고 다른 파일에서는 어떻게 활용될 지 알 수가 없으므로, 자동으로 final으로 동작시켜줄 수 없다. 이러한 단점을 극복한 것이 WMO라는 것이다. 다만 internal보다 상위 접근 제어자(public, open)이 선언된 경우에는 외부 모듈에서도 접근이 가능하므로 WMO를 사용해도 Dynamic Dispatch로 작동한다.
- (참고) https://velog.io/@wnsxor1993/Class%EC%9D%98-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EB%B0%A9%EB%B2%95
- (참고) https://velog.io/@leeesangheee/iOS.-class%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%84-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%AC%EC%88%98-%EC%9E%88%EB%8A%94-%EB%B0%A9%EB%B2%95%EB%93%A4%EC%9D%84-%EB%82%98%EC%97%B4%ED%95%B4%EB%B3%B4%EC%8B%9C%EC%98%A4
- 스위프트는 자동으로 메모리 할당과 해제를 처리한다. 메모리 할당과 해제는 Stack 또는 Heap에서 처리한다.
- Copy On Write는 어떤 방식으로 동작하는지 설명하시오.
- 실제 원본이나 복사본이 수정되기 전까지는 복사를 하지 않고, 원본 리소스를 공유함. 원본이나 복사본에서 수정이 일어날 경우 그 때, 복사하는 작업을 함. 이 작업이 swift에서는 Collection Type을 복사해서 사용할 때 일어남.
- (참고) https://babbab2.tistory.com/18
-
int array1 = [1,2,3] // array1의 주소: AA int array2 = array1 // array2의 주소: AA array1[0] = 1 // COW !! array2의 주소: BB // COW는 원본이나 복사본의 수정이 있을 때, 메모리를 할당함
method dispatch
static method dispatch
static method dispatchstatic method dispatch
- Convinience init에 대해 설명하시오.
- 초기화의 종류에ㅐ는 크게 Designated, convenience가 존재한다. 모든 멤버를 초기화하고 커스터마이징하기 위함.
- swift의 초기화 이니셜라이저로 본 Designated init(지정 이니셜라이저)는 모든 프로퍼티가 초기화될 수 있도록 합니다. 지키지 않을경우 error발생
- convenience init은 보조 이니셜라이저로 클래스의 원래 이니셜라이저의 init을 도와주는 역할. convenience init은 같은 클래스에서 다른 이니셜라이저를 호출해야한다는 규칙이 있음.
-
class Person { var name: String var age: Int var gender: String init(name: String, age: Int, gender: String) { self.name = name self.age = age self.gender = gender } convenience init(age: Int, gender: String) { self.init(name: "zedd", age: age, gender: gender) } }
- AnyObject에 대해 설명하시오.
- Swift는 Type에 민감하여 특정하지 않은 타입에 대해 동작하도록 특별한 타입 2가지 제공한다. 원래는 타입 캐스팅을 수행할 때, 일반적으로 상속 관계에 있는 클래스끼리만 캐스팅이 가능하지만, Any, AnyObject 타입을 사용할 경우 상속 관계에 있지 않아도 타입 캐스팅이 가능하다.
- Any : 함수 타입을 포함하여 모든 타입의 인스턴스를 나타낼 수 있습니다. Value타입 (struct, class), reference타입(class, closure) 둘 다 저장이 가능합니다. 한마디로 모든 타입을 포함할 수 있습니다.
- AnyObject는 모든 클래스 타입의 인스턴스를 나타낼 수 있는 프로토콜입니다. AnyObject로 선언 시, 클래스 타입만 저장할 수 있습니다. 따라서 클래스 타입이 아닌 구조체 열거형 ref 타입인 클로저는 AnyObject에 해당하지 않습니다.
- (Any, AnyObject의 단점) 매번 타입 체크 및 형변환을 해야한다. as나 as? 등으로 다운 캐스팅하여 사용한다.
-
var name: Any = "lgvv" // error // String 타입을 Any로 선언해주어도 name에 String 메서드나 프로퍼티에 접근하려고 하면 에러가 난다. // 이유는 Any, AnyObject는 런타임 시점에 타입을 결정하기 때문에 컴파일 시점에는 해당 타입을 알 수 없어서 // 따라서 접근할 수도 없다. // 올바른 사용법 if var name = name as? String { name.append("다운캐스팅") }
- Optional 이란 무엇인지 설명하시오.
- 장점 : 개발자가 런타임에 nil로 인한 문제를 컴파일 단계에서 예방 가능
- 옵셔널 바인딩 : 옵셔널 타입으로 선언된 변수에 값이 있는지 없는지를 체크
- 옵셔널 체이닝: 여러 객체를 혼합해서 사용 시 옵셔널끼리 연산이 필요할 때 if가 너무 많이 필요하게 되는데, ?통해서 해결함. 좀 더 간단하게 옵셔널을 예외처리할 수 있음.
- obj-c에서 아직 nil타입이 존재하며 obj-c와 swift의 상호 운영성을 위해 사용
- Struct 가 무엇이고 어떻게 사용하는지 설명하시오.
- 구조체이고 클래스와 구조체의 선택의 관점에서 설명해보자면, 캡슐화하기 위한 것이나 참조되기보다 복사되길 원하는 경우, 상속할 필요가 없는 경우에 사용한다.
- (참고) https://jusung.gitbook.io/the-swift-language-guide/language-guide/09-classes-and-structures
- Subscripts에 대해 설명하시오.
- 클래스, 구조체 그리고 열거형에서 스크립트를 정의해 사용할 수 있습니다. 서브스크립트란 콜렉션, 리스트, 시퀀스 등 집합의 특정 멤버 엘리먼트에 간단하게 접근할 수 있는 문법입니다
-
struct TimesTable { let multiplier: Int subscript(index: Int) -> Int { return multiplier * index } } let threeTimesTable = TimesTable(multiplier: 3) print("six times three is \(threeTimesTable[6])") // "six times three is 18" 출력
- (참고) https://jusung.gitbook.io/the-swift-language-guide/language-guide/12-subscripts
- String은 왜 subscript로 접근이 안되는지 설명하시오.
- Swift의 String은 Struct 타입이고, characters의 collection 타입이다. 즉 Array<Element>의 element가 Character인 배열이다.
- 하지만 Swift에서 String은 subscript, 즉 str[0]같이 Int로 접근하지 못하고, String.index로 접근해야한다. 그 이유는 Swift에서 Character는 1개 이상의 Unicode Scalar로 이루어져 있다. 즉 크기가 가변적이다.
-
let test = "🤡🤖123" let index = test.index(text.startIndex, offsetBy: 1) print(index) // Index(_rawBits: 1180673) print(test[index]) // 🤖
- (참고) https://velog.io/@hansangjin96/Swift-String%EC%9D%80-%EC%99%9C-subscriptInt%EB%A1%9C-%EC%A0%91%EA%B7%BC%EC%9D%B4-%EC%95%88%EB%90%A0%EA%B9%8C
- instance 메서드와 class 메서드의 차이점을 설명하시오.
- instance 메서드와 클래스, 구조체, 열거형 형태의 인스턴스에 속한 메서드를 의미함. 클래스를 통해 호출할 수 없고 클래스의 인스턴스를 만들어서 실체화(인스턴스)하여 생성된 인스턴스를 호출
- class 메서드는 인스턴스를 만들어서 실체화 하지 않아도 클래스를 통해 직접적으로 호출할 수 있다.
-
class func methodName() { }
- 인스턴스 메소드와 달리 struct와 enum에서 사용할 수 없는데, 왜냐하면 이 둘은 상속할 수 없기 때문이다. class 메소드는 static메소드를 상속할 수 있도록 만든 메소드이기 때문이다. swift에서는 static 메소드를 오버라이딩 할 수 있도록 클래스 메소드를 추가하였다. 즉, 클래스에서 한해서 스태틱 메소드를 오버라이드 하는 것이 가능하다.
- (instance 메서드와 class 메서드의 차이 정리)
- 정의된 Class, struct, enum 을 실체화(인스턴스) 하여 사용하느냐
- 실체화(인스턴스) 하지않고 직접적으로 호출 하느냐
- Class 가 아닌 struct, enum 에서 선언될 수 있느냐 없느냐
- class 메서드와 static 메서드의 차이점을 설명하시오.
-
class SwiftClass() { static func staticMethod() { } class func classMethod() { } } class DerivedSwiftClass: SwiftClass { override static func staticMethod() { } // 불가능 Cannot override static method override class func staticMethod() { } // 가능 }
- class메소드와 static 메소드는 타입 메소드에 속하며, static 메소드는 오버라이드가 불가능하다.
- (참고) https://babbab2.tistory.com/122
-
- Delegate 패턴을 활용하는 경우를 예를 들어 설명하시오.
- 개인적인 경험에서 테이블 뷰가 있고 셀이 여러개 있다고 치자. 그럼 테이블 셀에 버튼을 갖고 있다고 가정할 때, 내부 특정 버튼을 눌렀을 때, 특정셀의 데이터 정보를 갖고 처리할 수 없다. 이 경우 custom Delegate를 만들어서 처리가 가능하다.
- (참고)https://prod.velog.io/@hayeon/Delegate-%ED%8C%A8%ED%84%B4%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0%EB%A5%BC-%EC%98%88%EB%A5%BC-%EB%93%A4%EC%96%B4-%EC%84%A4%EB%AA%85%ED%95%98%EC%8B%9C%EC%98%A4
- Singleton 패턴을 활용하는 경우를 예를 들어 설명하시오.
- 특정 용도로 객체를 하나만 생성해서 공용으로 사용하고 싶을 때 사용하는 방법
- 즉, 인스턴스가 하나만 존재하는 것을 보증하고 싶은 경우에 사용하는데 환경설정, 네트워크, 로그인 정보 등을 특정 용도로 생성해둔 객체에 넣어두고 여러 객체에서 접근하도록 하여 데이터를 사용.
- (장점) 메모리 낭비를 맞고 데이터를 공유할 수 잇는 장점이 있음
- (주의) 한번 할당 된 메모리는 끝날 때까지 할당되어 있다.(프로세스가 종료될 때까지 남아 있음 -> 메모리 낭비 존재) 최초 할당 시점은 접근하기 시점에 초기화 되며, 더이상 사용하지 않을 때는 릴리즈한다. ex) 햅틱매니저 구현 생각하기
- KVO 동작 방식에 대해 설명하시오.
- KVO는 A객체에서 B객체의 키의 값이 변화됨을 감지할 수 있는 패턴입니다. KVO 패턴은 객체와 객체 사이의 관계를 다룰 때 적합합니다. 메소드나 다른 액션에서 나타나는 것이 아닌 프로퍼티의 상태에서 반응하는 형태
- 동작방식 : 모델 객체의 어떤 값이 변경되었을 경우 이를 UI에 반영하기 위해서 컨트롤러는 모델 객체에 Observing을 도입하여 델리게이트에 특정 메시지를 보내 처리할 수 있도록 하는 것
- 즉, 변수에 코드를 붙여 변수가 변경될 때마다 코드가 실행되도록 하는 방법을 의미한다. property observers(willset , didSet)과 아주 유사한데 KVO는 타입 정의 밖에서 observe를 추가한다는 점이 다르다. KVO는 순수 스위프트 코드로는 그리 좋지 않은데, 그 이유는 Objective-c 런타임에 의존 하고 있기 때문이다. 그래서 NSObject를 상속받기 위해 @objc 를 반드시 붙여줘야 한다. 특히 KVO는 속성 각각에 @objc dynamic 을 붙여줘야 한다.
- Delegates와 Notification 방식의 차이점에 대해 설명하시오.
- 두 객체 모두 객체간의 소통을 위해 만들어짐. 특정 이벤트가 발생하면 원하는 객체에 해당 사항을 전달해서 특정처리 수행하도록 구성된다.
- delegate 방식은 주로 이벤트를 1:1로 전달할 때 많이 사용됩니다. 주로 protocol을 정의하고 이를 이벤트를 대신 처리할 객체가 채택하여 사용하게 됩니다. 이에 따라 제 3 객체를 필요로 하지 않으며 확실한 처리가 가능하지만, 많은 줄의 코드를 필요로 하며 많은 객체에게 이벤트를 알리고 싶을 경우 비효율적이라는 단점이 있습니다.
- notification 방식은 이벤트를 1:N으로 전달할 때 용이합니다. NotificationCenter라는 싱글톤객체를 기반으로 이벤트 발생여부를 옵저버를 등록한 객체에게 전달하는 방식으로 구성됩니다. 따라서 다수의 객체에게 손쉽게 이벤트 전달이 가능합니다. 하지만 제 3 객체를 필수적으로 필요로 하며, key값으로 Notification의 이름을 사용하기 때문에 컴파일 중 구독이 제대로 이루어져있는지 확인할 수 없다는 단점이 존재합니다.
- (참고) https://github.com/lunchScreen/Interview_Questions/issues/59
- 멀티 쓰레드로 동작하는 앱을 작성하고 싶을 때 고려할 수 있는 방식들을 설명하시오.
- 멀티 스레딩을 사용하는 이유 : 시간이 오래 걸리는 작업 진행 시, 어플리케이션의 실행을 방해하지 않고 멀티 코어에서 큰 작업을 여러개로 분할하여 진행해야 하기에, 메모리 공간과 시스템 자원 절약하기 위해서.
- 어떤 작업을 글로벌 큐에 넣어야 하는지 정확히 알아둬야 하고, 글로벌 큐에 배치할 때에는 QoS를 적절하게 사용해야합니다. 또한 동기 및 비동기로 처리할지 명확하게 정의하고 상황에 인과관계를 설정하거나 특정 시간 이후에 처리하도록 설정합니다.
- 직접적으로 스레드를 관리하지 않고 큐를 활용하여 작업을 분산처리하고 GCD, Operation을 이용하여 iOS에서 알아서 스레드 숫자를 관리하고 다른 스레드들이 비동기적으로 동작하도록 만듭니다.
- (참고) https://velog.io/@hayeon/Delegate-%ED%8C%A8%ED%84%B4%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0%EB%A5%BC-%EC%98%88%EB%A5%BC-%EB%93%A4%EC%96%B4-%EC%84%A4%EB%AA%85%ED%95%98%EC%8B%9C%EC%98%A4
- MVC 구조에 대해 블록 그림을 그리고, 각 역할과 흐름을 설명하시오.
- MVC 패턴 Model - Controller - View 로 이루어져 있다.
- Model : 앱의 정보와 데이터(DB, 변수, 초기화 값 등) 사용자가 편집하고자하는 모든 데이터를 갖고 있어야 하며 view나 controller에서는 어떤한 정보도 알면 안된다. 또한, 변경이 일어나면 변경 통지에 대한 처리 방법을 구현해야 한다.
- Controller : 모델이나 뷰에 대해서 알아야 한다. 모델이나 뷰의 변경을 모니터링 해야한다. (Model과 View를 연결해주는 역할)
- View : 모델이 가진 정보를 따로 저장해서는 안되며, 모델이나 컨트롤러와 같이 다른 구성요소들을 몰라야 한다. 변경이 일어나면 변경 통지에 대한 처리 방법을 구현해야 한다. ( UI담당 )
- [Controller - Model]
- Controller는 Model에게 언제든지 이야기 할 수 있습니다.
- 모델의 모든 것을 알고있고, 보내고 싶은 메시지를 보낼 수 있습니다.
- Model은 Controller에게 이야기할 방법이 없습니다.
- 이유는 컨트롤러는 UI로직을 다루는데 모델과 UI는 별개이기 때문입니다.
- 하지만 UI와 상관없는 모델이 값이 바뀌는 데이터를 가지고 있다면 Notification 과 KVO 를 이용하여 컨트롤러와 소통할 수 있습니다.
- 즉 이 또한 컨트롤러에게 직접 말하는 방식이 아닌 방송처럼 그들에게 전달해줍니다.
- Controller는 Model에게 언제든지 이야기 할 수 있습니다.
- [Controller - View]
- Controller와 View 연결은 대부분 Outlet을 통해 이루어집니다.
- View와 Controller 역시 이야기할 필요가 있습니다.
- 이유는 뷰는 컨트롤러의 하위종속자이고, UI안에서 일어나는 것들을 Controller에게 전달해야하기 때문입니다.
- 따라서 뷰는 컨트롤러에게 이야기할때 눈에 보이지 않고 구조화되어야합니다.
- 눈에 보이지 않는다는 건 뷰에 있는 객체는 자신이 어떤 클래스에게 이야기하고 있는지 모른다는 뜻
- 구조화되어야 한다는 건 객체에 대해 아는 바가 없기에 제대로 미리 서술되어 있는 방식으로 의사소통 해야한다는 뜻
- target-action
- 컨트롤러가 @IBAction 코드로 메서드를 생성합니다(타겟).
- 뷰의 하위종속자 객체가 컨트롤러가 생성한 타겟메서드와 연결하면 뷰가 컨트롤러와 이야기하고 싶을 때 간단하게 타겟메서드를 호출하면 됩니다.
- 뷰로부터 컨트롤러로 보내지고 있는 액션은 보통 컨트롤 + 드래그로 연결합니다
- delegate
- 가끔 뷰는 단순히 터치되는 것 이상으로 더 복잡한 의사소통을 하기도 합니다.
- 예를 들어 뷰의 하위종속자인 스크롤 뷰는 다양한 정보들을 컨트롤러에게 이야기합니다. 왜냐하면 컨트롤러는 그것에 대해 알고 반응해야 하고, 정보가 모델에 영향을 줄 수도 있기 때문입니다.
- 따라서 주고받는 정보들이 많을 것이고, should, will, did 같은 말이 포함될 것입니다.
- 뷰의 하위종속자들은 컨트롤러에게 다양한 질문을 하고 싶어하는데 should, will, did 같은 말이 포함된 질문들은 델리게이트라는 것을 통해 이뤄지게 됩니다.
- delegate는 위임이라는 뜻인데, 뷰의 하위종속자들이 컨트롤러에게 어떤 책임을 위임하고 있기 때문에 이러한 단어를 사용합니다.
- 델리게이트는 뷰 안에 있는 프로퍼티입니다.
- datasource
- 뷰는 자신이 보여주고 있는 데이터를 소유할 수 없습니다.
- 뷰는 컨트롤러에게 매번 물어보고, 컨트롤러는 모델에서 데이터를 가지고 와서 표시합니다.
- 이것은 델리게이트와 다른종류의 프로토콜입니다.
- should, will, did 대신 다른 프로토콜의 메시지를 받습니다.
- 뷰는 데이터에 대해 이곳에 있는 데이터를 달라고하거나, 데이터의 개수가 몇개인지 물어봐서 무슨 일이 생기고 있는지 알게되고, 그걸 화면에 보여줄 것입니다.
- 이것 또한 델리게이션 과정으로 이루어지는데 여기서 쓰이는 델리게이트가 바로 데이터소스입니다.
- 프로토콜이란 무엇인지 설명하시오.
- 특정 역할을 하기 위한 메소드, 프로퍼티, 기타 요구사항 등으로 조건을 정의를 할 뿐 스스로 기능을 구현하진 않는다.
- 하지만 프로토콜에 정의한 프로퍼티, 메소드는 필수로 정의해야하는 조건입니다.
- 프로퍼티를 정의할 때는 get / set 키워드를 사용해 반드시 명시하고, 메소드를 정의할 땐 구현이 없게끔 작성하고, static키워드를 사용함으로써 인스턴스 메소드나 타입 메소드가 되도록 정의할 수도 있음.
- 구조체, 클래스 ,열거형은 프로토콜을 채택해서 요구사항을 실제로 구현할 수 있음.
- value타입인 구조체, 열거형일 경우, 메소드가 메소드 자신이 속해 있는 인스턴스를 변경하고자 의도하는 경우에는 반드시 메소드 정의부 앞 부분에 mutating 키워드를 추가해야 한다.
- class만 @objc 속성을 사용하는 프로토콜 채택이 가능합니다.
protocol testDelegate { var test: Int { get } var test2: Int? { get } func textFunction(hello: String) -> String } extension testDelegate { var text: Int { return 3 } func textFunction(hello: String) -> String { return "test \(hello)" } } class testClass: testDelegate { var text2: Int? } let example: testClass = testClass() print(example.test) // 3 print(example.test2) // nil print(example.textFunction(hello: "hi")) // test hi
- 프로토콜에도 extension을 적용할 수 있다. extension에서 옵셔널 부분을 정의해주고 있다.
- Protocol Oriented Programming과 Object Oriented Programming의 차이점을 설명하시오.
- Protocol Oriented Programming은 프로토콜 중심 프로그래밍, Object Oriented Programming은 객체 중심 프로그래밍입니다. POP는 프로토콜 확장을 통하여 수평 구조로 타입을 확장하고, OOP는 슈퍼클래스의 상속을 통하여 수직 구조로 타입을 확장하는 방식으로 다형성을 구현 합니다.
- OOP는 상속을 기본 전제로 가져가기 때문에 Super Class와 Sub Class가 생성됩니다. 이 때 Sub Class는 Super Class를 그대로 상속받기 때문에 자신에게 필요하지 않은 property나 method를 가질수 없습니다. 하지만 POP는 protocol에 정의된 인터페이스를 직접구현하는 것이 전제입니다. 따라서 필요하지 않은 property나 method를 갖지 않도록 구현할 수 있습니다. 또한 상속에서는 하나의 Sub Class는 하나의 Super Class만을 가질 수 있지만 Protocl은 그렇지 않습니다.
- (참고) https://github.com/lunchScreen/Interview_Questions/issues/28
- Hashable이 무엇이고, Equatable을 왜 상속해야 하는지 설명하시오.
- Hashable이란 Set, Dictionary 등 Collection 구조에서 임의의 자료형을 정수의 값으로 변환하여 Key값을 통하여 각각의 인스턴스를 접근할 수 있도록 해준다. 또한 Hashable은 Equatable의 프로토콜을 준수하여야 하는데, 이는 Hashable을 통해서 변환된 정수의 값을 바탕으로 인스턴스를 비교하여 찾아야 하기 때문이다.
- Hashable의 딕셔너리에서 Key값을 설정할 때 자주 언급된다.
- (참고) https://dev-dream-world.tistory.com/68
- mutating 키워드에 대해 설명하시오.
- 클래스는 ref 타입이고 struct와 enum은 value type이다. value 타입의 속성은 기본적으로 메서드 내에서 수정할 수 없다. 만약 수정해야 하는 경우 mutating을 붙여주면 됩니다. mutating을 선언한 메서드는 메서드 내에서 프로퍼티를 변경할 수 있다. mutating을 선언한 메서드는 메서드 내에서 프로퍼티를 변경할 수 있고, 메서드가 종료될 때 변경한 모든 내용을 원래 struct에 다시 기록합니다. 또한 메서드는 self property에 새 인스턴스를 할당할 수도 있다.
- mutating 키워드는 해당 메서드가 호출된다면 실제 복사를 해야한다고 알려주는 역할
- 탈출 클로저에 대하여 설명하시오.
- 함수의 인자로 전달된 클로저가 함수가 반환된 후 실행되는 클로저, 전달인자로 받은 클로저가 함수 종료 후에 호출될 경우 "클로저가 함수를 탈출한다"로 표현
- (사용법) @escaping 키워드를 사용하여 명시
- (언제 탈출할까?) 전달 받은 클로저가 클로저 함수 외부로 다시 반환되는 경우, 외부 글로벌 변수에 저장되는 경우
-
// @escaping 선언이 없다면 구문 밖에서 사용이 불가능하기 때문에, 배열에 할당이 불가능합니다. var completionHandlers: [() -> Void] = [] func withEscaping(completion: @escaping () -> Void) { completionHandlers.append(completion) }
- (왜 탈출 시킬까?) 보통은 비동기 작업을 위해서 사용
-
private func requestSignup(_ url: String, _ headers: HTTPHeaders?, _ parameters: Parameters?, _ completion: @escaping (NetworkResult<Codable>) -> Void) { guard let url = try? url.asURL() else { return } AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers) .validate(statusCode: 200...500) .responseDecodable(of: APIResponseData<APICantSortableDataResult<SignupResponse>, APIError>.self) { response in switch response.result { case .success(let signupResponse): guard let statusCode = response.response?.statusCode else { return } if statusCode == 200 { completion(.success(signupResponse.data?.result)) } else { completion(.requestErr(signupResponse.error?.errorMessage)) } case .failure: completion(.networkFail) } } }
- 이렇게 탈출 클로저를 사용해 결과를 받아오고 결과에 맞는 분기처리로 해당 함수 구문 밖에서도 적절한 동작을 할 수 있도록 설계할 수 있다
- Extension에 대해 설명하시오.
- 클래스, 구조체, 열거형, 프로토콜 타입에 새롭게 기능 등을 정의, 특정 타입의 기능 및 준수하는 프로토콜별 구현부를 분리해서 보다 코드 가독성을 높일 수 있음.
- 저장 프로퍼티를 선언할 수 없음.
- Extension 내부에서 함수를 override할 수 있는지 설명하시오.
- 애플 공식 문서에서는 "extension은 타입에 새로운 기능을 추가할 수 있으나, 기존에 존재하는 기능을 Override할 수 없다"고 명시되어 있으나, 개발에서는 extension을 통해 override가 가능하다. 그 이유는 obj-c와 호환성을 위해서 그러나, 애플에서는 익스텐션은 기능의 추가를 위해서 사용하라고 하니 익스텐션에서 오버라이드는 지양하자.
- (참고) https://feather.tistory.com/2
- 접근 제어자의 종류엔 어떤게 있는지 설명하시오.
- 1. private : 구현한 범위 내에서만 사용 가능
- 2. fileprivate : 구현된 소스파일 내에서만 사용 가능
- 3. internal(default) : 소스파일이 속해 있는 모듈 어디서든 사용할 수 있음, 외부 모듈에서는 접근 불가
- 4. public : 어디서든 사용될 수 있고, 주로 프레임워크에서 외부와 연결된 인터페이스를 구현하는데 많이 쓰임
- 5. open : 클래스와 클래스 멤버에서만 사용 가능, 모듈 밖의 다른 모듈에서도 재정의 가능, open을 명시하는 것은 클래스를 다른 모듈에서도 부모 클래스를 사용할 목적으로 설계했다는 것을 의미
- (1,2,3)은 Application development (4,5) Framework develpment에서 사용
- defer란 무엇인지 설명하시오.
- defer란 현재 코드 블록을 나가기 전에 꼭 실행해야 하는 코드
- defer가 호출되는 순서는 어떻게 되고, defer가 호출되지 않는 경우를 설명하시오.
- defer는 선언된 역순으로 실행된다. 즉 아래 -> 위, 중첩의 경우는 defer에서 내부 -> 외부 순서이다.
- 호출되지 않는 경우는
- 1. throw를 이용해서 오류를 던질 경우
- 2. guard문을 사용하여 중간에 함수를 중단하는 경우
- 3. 리턴값이 never(비반환함수)의 경우
- property wrapper에 대해서 설명하시오.
- 반복적으로 나타나는 property 구현 패턴에 대한 집합을 컴파일러에 하드코딩 하는 대신, 이러한 패턴을 라이브러리로 정의할 수 있는 일반적인 메커니즘을 제공하는것.
- @키워드를 사용하여 SwiftUI에서 @State @Binding @EnvironmentObject 등의 많은 Property wrapper를 사용한다.
- Generic에 대해 설명하시오.
- 포괄적인이란 의미를 가진 뜻으로 Swift의 강력한 기능 중 하나.
- 제네릭을 이용한다면 타입에 유연하게 대처하는 것이 가능해진다. 제네릭으로 구현한 기능과 타입은 재사용에 용이하며, 코드의 중복을 줄일 수 있어 깔끔한 표현이 가능하다. 또한 제네릭은 타입 제약을 걸어줄 수도 있다. 타입 제약은 클래스 타입 또는 프로토콜만 가능하다. (구조체 혹은 열거형 등의 타입은 사용이 불가능)
-
func swap<T>(_ a: inout T, _ b: inout T) { let temp: T = a a = b b = temp } var first = 1 var second = 2 swap(1,2) print("\(first) \(second)") // 2 1 var str1 = "str1" var str2 = "str2" swap(str1, str2) print("\(str1) \(str2)") // str2 str1 // 타입을 제약하고자 하는 경우 func substract<T: BinaryInteger>(_ a: inout T, _ b: inout T) -> T { return a - b }
- some 키워드에 대해 설명하시오.
- swiftUI에서 body에 위치한 some키워드를 자주 보았는데 우선 body 프로퍼티는 computed property로써 view의 contents를 return한다. some 키워드는 리턴 타입을 자동으로 그리고 빠르게 추론할 수 있는 스위치 기능
- some이라는 키워드는 명확하지 않은 타입으로 프로토콜에 정의되어 있고, 함수나 연산 프로퍼티를 만들어서 이 프로토콜을 반환 타입으로 갖고 싶을 때 아직 명확하지 않은 타입이라서 컴파일 과정에서 오류가 발생하게 되는데 그 오류를 없애기 위해 그 역할을 명확하게 만들어주고자 사용하는 것이 some키워드이다. 이를 통해 컴파일러에게 함수 혹은 프로퍼티는 동일한 명확한 타입을 가진 값만을 반환한다는 것을 알려준다.
- (정리) 컴파일러에서 concrete type을 반환한다고 확신을 주는 키워드
- (참고) https://peachberry0318.tistory.com/18
- Result타입에 대해 설명하시오.
- Swift에서 제공해주는 타입으로 success와 failure로 나누어져 있고, 이 두가지 케이스에 대한 연관된 값을 표현한다. enum으로 만들어져 있다.
- Codable에 대하여 설명하시오.
- swift4에서 나와서 Encodable과 Decodable이 합쳐진 것.
- Encodable : data를 Encoder를 통해 변환해주려는 프로토콜로 바꿔주는 것
- Decodable : data를 원하는 모델로 Decode해주는 것
(참고)
https://jusung.gitbook.io/the-swift-language-guide/
'deprecated > 잡동사니' 카테고리의 다른 글
iOSInterviewquestions - ARC (0) | 2022.04.02 |
---|---|
iOSInterviewquestions - Rx (0) | 2022.04.02 |
iOSInterviewquestions - Autolayout (0) | 2022.04.02 |
[iOS] 🗺 KakaoMap API 환경설정 (0) | 2022.02.18 |
[Swift] kakaoMap API URL Scheme 간단 사용법. (0) | 2022.01.17 |