32YB SOPT/iOS 세미나

[SOPT] iOS_1주차_과제 (final/private/lazy var/let/viewController/forEach/$0)

신_이나 2023. 4. 7. 21:24

 

 

SOPT iOS 세미나 1주차 기본과제입니다.

iOS 32YB 신지원 작성

 

 

0. 세미나 1주차 과제2 내용

 

 

 

 

1. final 키워드 사용 이유

 상속을 방지하기 위해서 사용한다. final로 선언된 요소들은 직접 호출하는 반면, 그렇지 않은 요소들은 vtable을 통해 간접 호출되어 직접 호출되는 경우보다 느리게 작동한다. 즉, 다른 class에서 상속과 overrider가 불가능하게 된다. vtable 은 오버라이딩에 따라 어떤 지점에서 실행할지를 결정하는 반면, final은 바로 어떤 메서드를 실행할지 결정할 수 있다.  따라서 상속, 오버라이딩 될 필요가 없는 클래스, 메서드, 프로퍼티에 final 키워드를 붙이게 된다면 클래스의 경우 static Dispatch로 작동하게 되어 런타임 시간이 더 효율적으로 작동된다.

 

 

 

2. private 키워드 사용 이유

 같은 파일 내부에서 private과 fileprivate 접근수준은 사용할 때 분명한 차이가 존재한다. fileprivate는 같은 파일 어떤 코드에서도 접근 가능하지만 private는 같은 파일 내부에 다른 타입 코드가 있어도 접근 불가능하다. 따라서, private는 같은 파일에도 접근이 불가능하지만 Extension 코드가 같은 파일에 존재하는 경에는 접근 가능하다. 이는 선언된 괄호 안에서만 사용이 가능하다!! 따라서 다른 클래스에서 참조하지 않는 프로퍼티나 메소드는 private로 선언하여야 한다. 이렇게 private를 사용하게 되면 정보은닉효과와 접근성 차별화를 둘 수 있다.

 

 

 

 

2-1. 다른 접근제어자들

 

open, public - 모든 모듈에 접근 가능

internal - entity가 작성된 모듈만 접근가능

fileprivate -entity가 작성된 파일에 접근가능

 

 

 

 

3. lazy var, let 차이점

- 시퀀스와 동일한 요소를 포함하짐나 일부 작업이 느리게 구현되도록 하는 시퀀스다.

 

 lazy var 를 이해하기 위해선, lazy 와 var 각각의 개념을 이해하도록 하자. lazy 란, 인스턴스를 생성하는 시점에 값을 부여하고 싶지 않은 변수를 선언하는 키워드다. 즉, 필요하지만 사용하기 전에 불러오고 싶지 않은 것들을 lazy로 선언하는 것이다. 따라서 lazy라는 값이 붙으면 초기 값을 주지 않고, 첫 번째 호출 때 초기화를 한다는 의미를 가지며 지연 저장 프로퍼티라고 말한다. lazy는 인스턴스를 생성할 때 네트워크를 통해 데이터를 가져와야 할 때 같이 오래 걸리는 작업에 의한 프로퍼티에 lazy를 선언하면 실제 사용하기 전까지는 실행되지 않는 편리함을 가진다. 편리함을 가질 뿐만 아니라 성능적 이점도 갖는데, 불필요한 성능저하나 공간 낭비를 줄일 수 있다.

[그렇기에 lazy 변수로 한 번 초기화 되면 항상 같은 값을 가질 거라고 생각할 수 있는데, 프로퍼티에 접근할 때마다 클로저가 실행되기 때문에 이때마다 다른 값을 출력할 수도 있다.(요곤 좀 더 알아보자,,,)]

 

<lazy 를 사용하기 위해선 몇 가지를 꼭 지켜주어야 한다.>

- var와 함께 쓰여야 한다. => let은 값이 변하지 않으며 처음부터 선언되기 때문에 재선언되고 업데이트 될 수 있는 var 를 써주어야 한다.

- class 와 struct 에서만 사용할 수 있다.

- lazy에 값을 넣어주기 위해서는 closure를 사용한다.

 

var 와 let

 var 와 let은 각각 변수와 상수를 의미하기 때문에 같이 설명하도록 하자. var은 변수로 후에 값을 변경 가능하지만, let은 상수로 후에 값을 변경할 수 없다. 따라서 위에 언급하였듯이, lazy는 var와 함께 사용해야 한다. 하지만 let도 객체를 다를 때 멤버에 직접 접근하여 변경이 가능할 수는 있다.

 

 

 

 

4. ViewController의 생명주기

 

수업 시간에 배웠던 생명 주기
구글링하여 찾아낸 생명 주기

 

viewDidLoad()

위의 그림에서는 보이지 않지만 뷰 컨트롤러를 생성했을 때 자동으로 생성되어 있는 메서드이다. 추가로 초기화하고 싶은 작업을 수행하며 보통 딱 한번 호출될 행위들을 작성한다. 더욱 추가적인 초기화를 진행할 때는 이곳에서 수정하여 재정의하곤 한다.

 

viewWillApear()

뷰가 화면에 나타날 거라는 신호를 컨트롤러에게 알리는 역할을 하는 메서드이다. 화면에 표시되기 전에 처리해야 하는 작업을 할 수 있으며 데이터 업데이트 등이 이 메서드에 해당한다. 이 함수는 반복해서 실행되며 뷰 컨트롤러가 화면에 나타날 때마다 실행한다.

 

viewDidAppear()

뷰가 화면에 나타났음을 알리는 메서드다.

 

viewWillDisappear()

뷰가 제거 될것임을 알리는 메서드이며, 뷰가 계층에서 사라지기 직전에 수행한다. 

 

viewDidDisappear()

뷰가 계층에서 사라진 뒤에 호출되는 메서드이다. 뷰가 사라지는 것과 관련된 작업을 해당 메서드에 정의하며 뷰를 숨기는 작업을 수행한다. 위와 같은 메서드를 직접 사용할 때는 override 키워드와 super 라는 키워드를 사용할 수 없다.

 

 

 

5. forEach, $0

forEach란 for문 처럼 동일한 순서로 sequence 내의 각 요소에 대해 주어진 클로저를 호출한다. 하지만 for 문 자체와는 차이가 있다. ForEach 자체가 일종의 View Container처럼 작용하며, View를 계산해서 보여준다는 것이다. 또한, forEach 는 break, continue 를 사용할 수 없고, return을 통해 빠져나갈 수 있다. 

 

<=> 반면 for in 함수는 collection을 이용하여 호출하며 각각의 element의 개수만큼 직접 호출한다. 또한, break, continue 를 사용하며 반대로 return은 사용할 수 없다. 

 

예시로 살펴보자

 

i) 배열내 요소가 여러 개인 경우

let numberWords = ["one", "two", "three"]
for word in numberWords {
    print(word)
}
// Prints "one"
// Prints "two"
// Prints "three"


numberWords.forEach { word in
    print(word)
}
// Prints "one"
// Prints "two"
// Prints "three"

 

for in 과 forEach가 똑같은 결과로 나타난다. 하지만 이 둘은 다른 방식으로 작동하고 있다. 

 

 

ii) 배열 내 요소가 하나인 경우

let numberWords = ["one"]
for _ in 0...2 {
    print(numberWords[0])
}
// Prints "one"
// Prints "tone"
// Prints "one"

numberWords.forEach { word in
    print(word)
}
// Prints "one"

배열 내 요소가 하나인 경우에는 결과가 다르게 나타난다. for문은 element를 직접 계산하기 때문에 몇 번 출력할 지 제어할 수 있지만, forEach는 클로저를 이용하여 작동하기 때문에 요소의 수 만큼만 가능하다.

 

 

$0

swift는 Closure 표현식을 사용한다. 따라서 $0란 Closure의 단축 인자를 의미하며 Closure의 첫번째 인수를 가리킨다. $0, $1로 사용하게 되면 인자 값을 축약해서 사용할 수 있다. 

 Closure란 사용자의 코드 안에서 전달되어 사용되며 중괄호로 구분하는 코드의 블럭을 의미한다. 이는 나중에 다시 한 번 단독으로 다뤄보도록 하자!

 

 

 

5-1. 그 외 순수 고차함수

 

고차함수란? 변수로 지정할 필요없이 상수로 지정해서 변환할 수 있는 함수

 

bool 반환하는 것을 말한다. true 면 값을 포함하고 false면 값을 배제한다.

기존 컨테이너의 요소에 대해 조건에 만족하는 값에 대하여 새로운 컨테이너로 반환한다.

 

문자열이든 정수든 내부를 하나로 합쳐주는 기능을 한다. (총합, 총곱 등)

 

기존 컨테이너 요소에 대해 정의한 클로저로 매핑한 결과를 새로운 컨테이너로 반환한다.

기존 데이터를 변형하여 새로운 컨테이너를

생성할 수 있다.

 

 

 

 

 

5-2. $0.translatesAutoresizingMaskIntoConstraints = false 를 작성하는 이유

 

 "$0.translatesAutoresizingMaskIntoConstraints = false"를 알기 전에 우리는 Autoresizing와 AutoresizingMask 그리고 AutoLayout 에 대하여 알 필요가 있다.

 

 Autoresizing를 직역하면 '자동크기조정'의 의미를 지닌다. Apple은 개발자가 동적 레이아웃을 구성할 수 있도록 Autoresizing를 도입하였는데 쉽게 말해, superview 가 커지거나 줄어듦에 따라 subview 의 크기와 위치를 조정하는 방식을 결정하는 것이다. 예를 들어, 기종이 변경되거나, 세로 화면에서 가로 화면으로 바뀔 때 자동으로 크기가 조정된다. 이러한 Autoresizing은 AutoresizingMask를 통해 적용할 수 있다. AutoresizingMask란 슈퍼 뷰의 bounds가 변경 될 때 자신의 크기를 조정하는 방법을 결정하는 비트마스크이다. 다시 말해, 비트 마스크 또는 인코딩된 플래그 집합을 사용하는 레이아웃 방법으로, superview의 경계가 변경되는 상황에 작동 방식을 정의한다.

 

 

 AutoLayout 은 UI 요소들의 위치를 제약하고 UI 레이아웃을 결정하는 사용자 인터페이스 레이아웃 프레임워크다. superview 가 크기를 변경할 때 적절한 레이아웃을 계산한다. 효과는 위에서 언급한 것과 비슷하지만 두 가지는 다른 점이 있다.

 

출처

 

 이처럼 Autoresizing와 AutoLayout을 같이 사용된다면 충돌이 날 수 있다. View의 AutoresizingMask도 결국 Auto Layout의 constraints로 변환되면서 기존의 constraints와 레이아웃 충돌이 일어날 수 있기 때문이다. 따라서 AutoLayout을 사용하고자 할 때  translatesAutoresizingMaskIntoConstraints 를 false로 설정하여  "$0.translatesAutoresizingMaskIntoConstranints = false" 를 선언한 것이다. 이는 AutoresizingMask를 사용한 Constraints 변환을 막는 것으로 스토리보드에서는 오토 레이아웃이 적용된 View의 translatesAutoresizingMaskIntoConstraints를 자동으로 false 설정하게 된다. 

 

따라서 "$0.translatesAutoresizingMaskIntoConstranints = false" 가 없다면 위에서 언급하듯 두 가지의 layout이 충돌이나 중중복되거나 에러가 뜰 것이다.