Swift 면접질문들

Swift 면접 질문이라는데… 난이도는 그리 높지 않은 편이다.

http://www.toptal.com/swift/interview-questions

1

다음 코드를 보고 질문에 답하시오

var array1 = [1, 2, 3, 4, 5]
var array2 = array1
array2.append(6)
var len = array1.count
  • 문: len 값은 얼마이며, 왜 그런가요?
  • 답: 5. Swift의 Array는 값 시멘틱이며, 다른 변수에 대입하게 되면 복사가 일어난다. 따라서 array2array1과는 다른 독립된 사본이며, array2.append(6)가 실행되더라도 array1은 변하지 않는다.

2

다음 코드를 보고 질문에 답하시오

let op1: Int = 1
let op2: UInt = 2
let op3: Double = 3.34
var result = op1 + op2 + op3
  • 문: 어디에 에러가 있으며, 왜 에러인가? 그리고 어떻게 고쳐야 하나?
  • 답: 마지막 라인에 에러가 있다. op1, op2의 타입은 Int인데 op3Double 타입이다. 타입이 다른 두 수는 더할 수 없다. (더한 결과는 Int타입인가? 아니면 Double타입인가?) 이를 계산하려면 다음과 같이 수정한다.

    var result = Double(op1 + op2) + op3

혹은 다음과 같이 + 연산자를 오버로딩하는 방법도 있다.

func + (lhs:Int, rhs: Double) -> Double { return Double(lhs) + rhs }
var result = op1 + op2 + op3 // Double

3

아래 코드를 보고 질문에 답하시오.

var defaults = NSUserDefaults.standardUserDefaults()
var userPref = defaults.stringForKey("userPref")!
printString(userPref)

func printString(string: String) {
    println(string)
}
  • 문: 버그는 어디에 있나? 그리고 버그의 원인은? 어떤식으로 고쳐야 할까?
  • 답: 두 번째라인. defaults.stringForKey("userPref")!에서 해당 키가 없다면 nil을 리턴한다. 그런데 그 값을 강제로 unwrapping 하면 런타임 에러가 발생할 것이다. 옵셔널 값은 보다 안전하게 if let 구문을 이용해서 언래핑한다.
if let userPref = defaults.stringForKey("userPref") {
    printString(userPref)
}

4

  • 문: String 구조체는 countlength 프로퍼티를 제공하지 않고 대신 전역함수인 countElement<T>를 써야 한다. 이를 문자열에 적용했을 때 이 함수의 시간 복잡도는 어떻게 되는가? 그리고 그 이유는?
  • 답: O(n)이다. Swift의 문자열의 “길이”는 유니코드 문자 몇 개인가를 의미하는데, 유니코드 코드값은 2개 이상이 하나의 글자가 되는 경우가 있기 때문에 문자열 맵 전체를 확인해야 한다. Swift의 문자열은 각 글자(Character)들의 연결리스트에 가까운 구조이다.

Swift2.0에서부터 countElement() 함수는 제거되었다. CollectionType의 원소의 개수는 count 프로퍼티로 이동하였다. 문자열은 그 성격이 배열과 같은 집합은 아니므로 count 프로퍼티가 존재하지 않는다. 문자열의 글자의 수를 세고 싶다면 str.characters.count로 캐릭터맵의 원소의 개수를 이용한다.

5

  • 문: enum 타입에서 raw valueassociated value의 차이는?
  • 답: raw value는 개별 enum case가 대응되어 다른 case와 구분되는 값이다. associated value는 특정한 enum case와 연결되는 타입이다. 이 때 enum case는 해당 associated value에 대한 constructor 역할을 한다고 볼 수 있다. 즉, raw value의 경우 값이 다르면 다른 case에 해당하지만, associated value는 동일한 enum case 내에서 다른 값을 가질 수 있다.

6

AnyObject는 어떠한 클래스의 인스턴스라도 나타낼 수 있는 타입인데, 이는 내부적으로 프로토콜로 정의되어 있다. 이 때,

var array = [AnyObject]()
struct Test {}
array.append(Test())

이 코드는 컴파일 되지 않으며 다음과 같은 에러를 낸다.

Type 'Test' does not conform to protocol 'AnyObject'

그런데 다음 코드를 보면…

var array = [AnyObject]()
array.append(1)
array.append(2.0)
array.append("3")
  • 문: 이 코드는 문제없이 컴파일 된다. 왜?
  • 답: 이 문제는 틀렸다. Swift 2 부터는 Swift의 원시타입인 Int, Double 등은 자동으로 NSNumber로 브릿징되지 않으며 `as NSNumber`로 명시적으로 캐스팅해야 한다. 또한 이러한 캐스팅을 이용하려면 `Foundation`을 반입해야 한다. 그러한 전제없는 위 질문은 잘못되었다고 볼 수 있다.

7

struct Planet {
    var name: String
    var distanceFromSun: Double
}

let planets = [
    Planet(name: "Mercury", distanceFromSun: 0.387),
    Planet(name: "Venus", distanceFromSun: 0.722),
    Planet(name: "Earth", distanceFromSun: 1.0),
    Planet(name: "Mars", distanceFromSun: 1.52),
    Planet(name: "Jupiter", distanceFromSun: 5.20),
    Planet(name: "Saturn", distanceFromSun: 9.58),
    Planet(name: "Uranus", distanceFromSun: 19.2),
    Planet(name: "Neptune", distanceFromSun: 30.1)
]

let result1 = planets.map { $0.name }
let result2 = planets.reduce(0) { $0 + $1.distanceFromSun }
  • 문: result1, result2의 타입은 각각 무엇이며, 왜 그렇게 추론되는지 설명하세요.
  • 답: result1: [String], result2: Double.

먼저 result1은 맵핑에 사용된 클로저의 타입이 Planet -> String이기 때문에 [String]으로 추론할 수 있으며, result2는 각 거리값의 합인데, Swift는 기본적으로 실수값을 Double로 추론하기 때문이다.

8

아래 코드를 보고 문제에 답하라.

class Master {
    lazy var detail: Detail = Detail(master: self)

    init() {
        println("Master init")
    }

    deinit {
        println("Master deinit")
    }
}

class Detail {
    var master: Master

    init(master: Master) {
        println("Detail init")
        self.master = master
    }

    deinit {
        println("Detail deinit")
    }
}

func createMaster() {
    var master: Master = Master()
    var detail = master.detail
}

createMaster()
  • 문: 위 코드에는 어떤 버그가 있으며, 메모리에 영향을 미치는지? 그리고 어떻게 고쳐야 하는지?
  • 답: Master, Detail 두 클래스간에 순환참조가 있다. 이 관계에 있는 두 객체 인스턴스는 메모리 누수의 원인이 된다.

이는 한쪽에서는 다른쪽에 참조수를 늘리지 않도록 weak, unowned 조절자를 써서 프로퍼티를 정의해야 한다.

class Detail {
    unowned var master: Master

    init(master: Master) {
        print("Detail init")
        self.master = master
    }
    ...
}

unowned는 옵셔널 타입에 쓸 수 없다. (값이 항상 있음을 가정함)

9

아래 코드를 보고 질문에 답하라.

struct IntStack {
  var items = [Int]()
  func add(x: Int) {
    items.append(x) // Compile time error here.
  }
}
  • 문: 위 코드는 컴파일 에러가 난다. 왜 그런 것이며, 어떻게 고쳐야 하는가?
  • 답: struct 는 값 시멘틱이기 때문에 인스턴스 자신이 변경되는 동작에는 명시적으로 mutating 변경자가 붙어야 한다.
struct IntStack {
  var items = [Int]()
  mutating func add(x: Int) {
    items.append(x) // Compile time error here.
  }
}