becomeFirstResponder와 resignFirstResponder란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


회사에서 코드를 보는데 resignFirstResponder 함수를 사용하는것을 보고 도대체 이게 뭘까? 하고 궁금증이 생겼다..
키보드를 올리고 내릴때, 특히 내릴때 이 함수를 계속해서 호출하는데 이럴때 사용하는 방법 중 하나라고만 하고 정확한 정의를 모르곘어서 한번 찾아서 정리해야겠다고 생각했고… 그래서 정리해보려고 한다..!!

becomeFirstResponder

해당 윈도우에서 객체를 first responder로 만들것을 요청하는 함수
해당 객체가 첫번째 응답 responder이면 true, 아니면 false를 리턴한다.

여기서 responder는 이벤트에 대한 응답을 처리하기 위한 추상 인터페이스를 뜻한다.
UIView, UIViewController, UIApplication 객체들이 responder에 해당한다고 생각하면 된다.
이벤트가 발생하면 UIKit는 이를 처리하기 위해 우리 앱의 responder에게 이벤트를 보낸다.

현재의 객체를 first responder로 지정하려면 이 메소드를 호출하면 된다.
즉 UITextView나 UITextField에서 키보드를 올리는 액션을 취하기 위해서는 이 메소드를 호출하면 된다.

하지만 호출한다고 해서 무조건 first responder가 되는건 또 아니라고 한다. UIKit에게 현재 first responder에게 resign을 요청하지만 원하는 대로 되지 않는 경우도 있기 때문이다. 이 케이스는 UIKit 객체의 canBecomeFirstresponder()를 호출하는데, 이 참수는 기본적으로 false를 반환하기 때문에 객체가 first responder가 되는데 성공하기 위해서는 이 이후 이벤트가 먼저 이 객체로 전달되고 UIKit는 객체의 input view를 보여주려고 시도하게 된다.

resignFirstResponder

해당 객체에게 지금 윈도우의 첫번째 responder로서의 상태를 포기하라고 요청이 왔음을 알리는 함수
첫번째 responder 상태를 물려주면서 true를 리턴한다.

get, set, willset, didset 이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


get, set

swift내에서 get, set은 우리가 일반적으로 알고있는 getter, setter와 매우 유사하다.

var test: Int {
  get {
    return test
  }
  set (value) {
    test = value
  }
}

print(test)  // test의 value 출력
test = 123   // value = 123

이렇게 코드를 작성하면 아마 경고? 혹은 에러가 발생할 것이다.
get, set이 해당 프로퍼티에 직접적으로 연결되어 있기 때문에 get {}, set {} 에서 test에 접근하면 recursive하게 자기 자신의 get, set이 호출되기 때문에 위와 같은 방법은 사용하지 않는다. 따라서 이러한 점을 보완하기 위해 저장소라는 개념을 이용한다. 여기서 저장소는 get, set을 사용할 때 ㅇ녀산된 값을 저장할 변수를 의미한다.

var _text: Int  // 실제 값이 저장될 변수

var test: Int {
  get {
    return _test
  }
  set (value) {
    _test = value
  }
}

외부에서 test의 값에 접근하거나 새로운 값이 할당될 경우 실제 값이 저장되는곳은 _test이다.
즉, test는 _test에 값을 저장하기 위해 일차적으로 연산의 결과는 저장하는 저장소가 된다.

Type Casting 다시 공부해보기!

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


타입캐스팅이란?

타입 캐스팅은 인스턴스의 타입을 확인하거나, 해당 인스턴스를 슈퍼클래스나 하위 클래스로 취급하는 방법이다.
swift에서 타입캐스팅은 is, as 연산자로 구현하며, 타입캐스팅을 사용해 타입이 프로토콜에 적합한지의 여부도 알 수 있다.

is

타입을 체크하는 연산자.

런타입 시점에 실제 체크가 이루어지며 표현식이 타입과 동일하거나 타입의 서브클래스인 경우 true
이 외의 경우 false 값을 반환한다. 즉 is 연산자는 타입을 체크하는 연산자로 반환값은 bool 형이다.

let name: String = "zehye"

name is String  // true
name is Int   // false

즉 내가 원하는 타입인지 확인할 때 사용할 수 있으며 동일한 타입인지 확인하기 위해서도 사용이 가능하다.
그런데 더 나아가 표현식이 타입의 서브클래스인 경우에도 is 연산자를 통해 확인이 가능하다.

class Person {

}

class Teacher: Person {

}


let teacher: Teacher = .init()
teacher is Teacher  // true
teacher is Person   // true

이렇게 Person클래스를 상속받고 있는 Teacher클래스에서 생성된 teacher 인스턴스 또한
Person클래스의 서브클래스이기 때문에 Person으로 타입 체크를 해도 true를 반환한다.

as

업캐스팅 (Upcasting)

서브클래스 인스턴스를 슈퍼클래스의 타입으로 참조한다.
업 캐스팅은 항상 성공한다.
as 연산자를 사용해서 할수도 있다.

무슨말인가? 아래 예시를 살펴보자.

class Person {
  let name: String
  init(name: String) {
    self.name = name
  }
}

class Teacher: Person {

}

class Student: Person {

}

let people: [Person] = [
  Teacher.init(name: "박선생")
  Teacher.init(name: "김선생")
  Student.init(name: "이제자")
]

swift는 타입에 민감한 언어이기 때문에 위 예시처럼 people이라는 배열안에는 person이라는 타입의 인스턴스만 들어갈 수 있다.

그런데 Teacher, Student 타입의 인스턴스가 어떻게 들어가게 된걸까?
이게 가능한 이유는 바로 업캐스팅 때문이다.

Teacher와 Student라는 클래스는 분명 명백히 서로 다른 타입의 클래스이다.
이 둘의 유일한 공통점은 Person이라는 부모 클래스를 상속받고 있다는 것이다.

즉, 둘의 부모 클래스가 Person으로 동일하기 때문에 이 둘을 Person이란 클래스로 업캐스팅해서 묶어버린것을 의미한다.

아래 예시를 보자.

class Person {
  let name: String = "zehye"
}

class Teacher: Person {
  let subject: String = "English"
}

class Student: Person {
  let grade: Int = 1
}

요런 코드들이 있다고 할때

let human = Teacher.init() as Person

이 코드의 의미가 뭘까? 그건 바로
Teacher 타입의 인스턴스를 생성하지만, 이를 Person 타입으로 업캐스팅해서 human에 저장하겠다 라는 의미이다.

실제 human의 타입은 업캐스팅 된 Person의 타입이다.
즉, human이 Teacher란 서브클래스(Person이라는 슈퍼클래스 타입으로 참조하는) 업캐스팅을 한것으로
human의 접근범위가 Person 멤버로 한정된다.

human.name  // zehye
human.subject  // Value of type "Person" has no member 'subject'

즉, Person 클래스 멤버인 name에는 접근 가능하지만 Teacher 멤버인 subject에는 접근이 불가능하다.
따라서 이렇게 서브클래스의 인스턴스를 슈퍼 클래스의 타입으로 참조하는 것 을 업캐스팅이라고 한다.

업캐스팅은 항상 성공하기 때문에
(상속 관계에서 슈퍼 클래스의 멤버는 서브 클래스가 당연히 포함하기 때문에)
as를 사용해도 되고 직접 타입을 명시해서 사용해도 된다.

as : 컴파일 시점에 타입 캐스팅(업캐스팅)을 하며 실패할 경우 에러가 발생한다.

다운캐스팅(DownCasting)

슈퍼클래스 인스턴스를 서브 클래스 타입으로 참조한다.
업 캐스팅된 인스턴스를 다시 원래 서브 클래스 타입으로 참조할 때 사용한다.
다운 캐스팅은 실패할 수 있기때문에 as?, as! 연산자를 이용한다.

업캐스팅은 서브클래스의 인스턴스를 슈퍼클래스의 타입으로 참조하는것이었다면, 다운 캐스팅은 그 반대로 슈퍼클래스의 인스턴스를 서브 클래스의 타입으로 참조하는것을 의미한다. 그리고 이때 사용하는것이 타입캐스팅인 as? 흑은 as! 라는 연산자이다.

var teacher: Teacher = human as! Teacher

이렇게 as연산자 다음에 ? 혹은 !를 사용함으로써 Person타입으로 업캐스팅된 human 변수를 다시 하위 클래스인 Teacher 타입으로 변환해서 넣어주는것이 바로 다운캐스팅이다. 슈퍼클래스인 Person 타입의 인스턴스를 서브클래스인 Teacher 타입의 인스턴스로 참조한 것 !

따라서 아래와 같이 접근이 가능해진다.

teacher.subject  // English

그런데 문제는 다운캐스팅은 실패할 수가 있다.
그렇기 때문에 실패할 가능성이 있는 다운캐스팅은 업캐스팅과는 달리 as에 옵셔널이 붙은 것이다.

as?

런타임 시점에 다운캐스팅을 하며, 실패할 경우 nil을 반환한다.
컴파일 시점에는 성공/실패 여부를 알 수 없기 때문에 옵셔널 타입으로 선언을 해야한다.

as!

런타임 시점에 다운캐스팅을 하며, 실패할 경우 에러를 발생시킨다.
따라서 가급적 as?를 사용하는 것이 안전한 방법이다.

asyncAfter의 deadline과 wallDeadline의 차이

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


asyncAfter

기본적인 dispatchQueue만 사용해보다가 asyncAfter를 접하게 되었고, 단순히 deadline을 통해 시간차를 두고 동작을 실행시킨다?
정도로만 이해하고 넘어가 있었다. 그런데 찾아보니 deadline 말고도 wallDeadline이란게 있네??? 이게 뭐지?

공식문서를 찾아보니 각각이 의미하는 내용은 같지만 파라미터와 타입이 다른것을 확인할 수 있었다.

func asyncAfter(deadline: DispatchTime, execute: DispatchWorkItem)

func asyncAfter(wallDeadline: DispatchWallTime, execute; DispatchWorkItem)

각각의 정의를 살펴보면 아래와 같다.

  • DispatchTime: (Structure)A point in time relative to the default clock, with nanosecond precision
    • struct, 나노세컨드의 정밀도를 가진 시스템 시계에 상대적인 시점 > 시스템 시계에 의존하지 않음
  • DispatchWallTime: (Structure)An absolute point in time according to the wall clock, with microsecond precision
    • struct, 마이크로세컨드의 정밀도를 가진 wall time에 따른 절대 시점 > 시스템 시계에 의존함

wall time은 컴퓨터 프로그램의 시작부터 끝까지 걸린 실제 시간입니다.
즉, 작업이 완료된 시간과 작업이 시작된 시간의 차이입니다.
따라서 벽 시간은 프로세서가 특정 작업에 대해 적극적으로 작업하는 시간만을 측정하는 CPU 시간과 다릅니다.

위키 백과에서 찾은 내용이다.

시스템 시계에 의존한다는 것은 무슨 의미를 뜻할까?

n초 동안 숫자는 1부터 +1 되며 카운트가 되어 프린트 찍힌다고 생각해보자.

1
2
3
...

이런식으로!

숫자가 하나씩 카운트 되고 있는 상황에서 시스템 시계를 바꿔놓는다고 생각해보자.
그러면 그 즉시 wallDeadline은 발생하지만 deadline은 발생하지 않는다.
즉 deadline은 시스템 시계에 의존하지 않기 때문에 시스템 시계의 설정이 변한다 할지라도 아무런 영향을 받지않지만
wallDeadline은 시스템 시계에 의존하기 때문에 시계의 설정이 변함과 동시에 영향을 받게 된다.

즉, 시스템 시계를 기준으로 그 절대 시점을 보느냐, 상대시점을 보느냐가 두 메서드의 관건이다.

deinit이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


deinit이란?

Deinitialization: 소멸자

소멸자는 클래스 인스턴스가 메모리에서 해제될때 즉시 호출되는 함수이다.
swift의 소멸자는 deinit 키워드를 이용해 사용할 수 있다. 작성방식은 init과 비슷하다!
swift의 소멸자는 구조체가 아닌 클래스 타입에서만 작성이 가능하다.

작동방식

swift는 특정 인스턴스가 더이상 필요없을때 메모리 공간 확보를 위해 해당 인스턴스를 자동적으로 메모리 해제시킨다.
이를 우리가 아는 ARC라고 하고 이 ARC는 전형적으로 특정클래스 인스턴스를 메머리 상에서 해제하고 싶을 때 수동적으로 해당 인스턴스를 정리할 필요가 없게 만든다. 즉 ARC는 더이상 필요없는 인스턴스에 대해서 자동으로 메모리 할당/해제 코드를 실행해준다. 하지만 앱이 자원을 사용하며 작동되고 있을때에는 개인적으로 따로 메모리를 관리해줄 필요가 있다.

클래스의 소멸자는 하나의 클래스 인스턴스 당 최대 하나씩 존재한다. 이러한 소멸자는 별도의 매개변수나 괄호를 명시하지 않는다.

deinit {
  // perform the deinitialization
}

소멸자는 해당 클래스 인스턴스의 메모리 해제가 필요할 때 자동으로 호출된다.
즉, 인스턴스가 메모리 해제되기 직전에 호출된다.

해당 소멸자를 직접 호출하는것은 허용하지 않는다.
상위 클래스의 소멸자는 그들의 서브클래스에 의해 상속된다. 또한 상위 클래스 소멸자는 자동적으로 마지막 서브클래스의 소멸자 구현부에서 호출된다.
상위 클래스의 소멸자는 항상 호출되는데, 아래의 서브클래스 소멸자에서 구현이 없을때에도 호출된다.

소멸자가 호출되기 전까지 인스턴스는 메모리에서 해제되지 않기 때문에 소멸자는 호출되는 인스턴스의 모든 프로퍼티에 접근이 가능하다.
또한 해당 프로퍼티들에 기반한 행위의 변경 또한 가능하다.

class Person {
  var name: String
  var age: Int

  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }

  deinit {
    print("deinit person class instance")
  }
}

// init call
var zehye = Person(name:"zehye", age: 20)

// deinit call
zehye = nil
  • zehye는 Person 클래스의 인스턴스다.
  • 인스턴스 생성 후 해당 인스턴스에 nil을 할당함으로써 메모리를 해제한다.
  • nil을 할당하면 deinit 소멸자가 호출된다.
  • deinitd에 구현해놓은 print()가 호출된다.
  • 콘손에 해당 문구가 출력된다.

그런데 만약 특정 클래스의 인스턴스 사용이 필요없어져 해체를 하고싶은데 deinit이 호출되지 않았다면 이때는 강한참조순환을 의심해보자!
이럴때에는 deinit메서드에 콘솔 출력코드를 두어 메모리 해제여부를 확인해보는것이 방법이다.