final
키워드의 적용extension
을 활용한 protocol conformance의 분리미리 관심가는 분야를 공부해두었더니 활동 학습에서 복습하며 돌이켜볼 수 있었습니다.
학습 내용은 이전에 정리해둔 내용과 동일한 내용입니다.
[Swift] 고차함수와 클로저 축약 표현
진행 중인 프로젝트의 클래스 다이어그램을 업데이트 하였다. 다이어그램 작성은 draw.io를 이용하고 있다.
프로젝트를 수행하며 텍스트 요소를 다루는 UI 요소 중 무엇을 선택하여 사용하여야 할지 고민하게 될 때가 있습니다. 어떤 상황에서 사용하여야 할지 결정하기 위해 간단하게 차이점을 정리해보겠습니다.
읽기 전용인 Static Text String을 보여줄 때 사용합니다.
편집 가능한 한 줄 텍스트 공간을 사용자에게 제공할 때 사용합니다.
편집 가능한 여러 줄 텍스트 공간을 사용자에게 제공할 때 사용합니다.
UIScrollView
를 상속하고 있어 기본적으로 스크롤 기능을 제공합니다.isSelectable
프로퍼티를 제공하여 사용자가 텍스트를 선택하게끔 지원할 수 있고, 이를 통해 복사, 공유 등의 작업을 할 수 있도록 도와줍니다. 물론 비활성화하여 UILabel
처럼 텍스트를 선택하지 못하게 설정할 수 있습니다.아래 이미지는 UITextView
가 제공하는 기능의 일부입니다.
지금까지 iOS에서 활용할 수 있는 세 가지 텍스트 관련 UI 요소를 알아보았습니다.
두 번째 Step을 수행하고 코드 리뷰를 받기 위해 PR을 보냈습니다.
Scroll View
의 Content Layout Guide
를 활용하여 스크롤 뷰를 구현하는 방법Scroll View
안에 포함된 Text View
가 Intrinsic size
를 가지게 하는 방법 -> Text View
의 Scrolling Enabled
속성 비활성화Navigation Controller
적용 방법View Controller
를 선택하고 Xcode 상단의 Editor
-> Embed In
-> Navigation Controller
선택Table View
에서 다음 뷰 컨트롤러에 데이터를 전달하는 방법만국박람회 프로젝트 Step 2는 세로 화면에 대응하는 화면과 기능을 개발하는 것이었습니다.
final
키워드의 적용타 프로그래밍 언어에서와 마찬가지로 Swift 또한 클래스의 상속을 지원합니다. 더 이상 해당 클래스에서 하위 클래스로의 전체 클래스 또는 일부 (메서드, 프로퍼티)가 상속 또는 상속에 따른 재정의가 될 필요가 없다고 판단한 경우 final
키워드를 각 요소 앞에 추가함으로써 상속을 방지할 수 있죠. The Swift Programming Language (Swift 5.4) - Inheritance에서는 아래와 같이 설명하고 있습니다.
Source: The Swift Programming Language (Swift 5.4) - Inheritance
final
키워드 적용이 성능에 미치는 영향그럼 final
키워드 적용을 하면 성능에는 어떤 영향을 미칠까요? 비록 공식 문서에서는 성능과 관련된 사항을 언급하고 있지 않지만, 이 문서에서는 final
로 선언된 요소들은 직접 호출하는 반면, 그렇지 않은 요소들은 vtable
을 통해 간접 호출되어 직접 호출되는 경우보다 느리게 작동한다고 소개하고 있습니다.
vtable
은 가상 메서드 테이블을 이르는 것으로, virtual method table, virtual function table, virtual call table, dispatch table, vftable 등으로 부르기도 합니다. 메서드 오버라이딩에 따라 실행 시점에 어떤 메서드를 실행할지 결정하는 동적 디스패치를 지원하기 위해 프로그래밍 언어에서 사용하는 메커니즘이죠.
상기 언급한 바와 같이 오버라이딩한 메서드는 실행 시점에 어떤 메서드를 실행할지 결정하는 반면, final
키워드가 적용된 메서드는 컴파일 시점에 어떤 메서드를 실행할지 결정할 수 있으므로 성능 상 이점을 가진다고 할 수 있습니다.
Source: Raywenderlich Swift Style Guide
Swift의 protocol
을 활용하면 class
를 상속하여 사용하는 것에 비해 다른 타입에 대한 의존도를 상당히 낮출 수 있습니다. 서로 다른 타입에서 동일한 프로토콜을 적용하여도 최소한의 요구사항만 만족시키면 되기에 채택하는 타입에서 자유롭게 구현 방식을 달리해도 되죠. 복수의 protocol
를 채택하는 경우 타입 내 프로퍼티와 메서드가 많아져 가독성이 떨어질 수 있는데요, 이런 경우에는 extension
을 활용하여 해당 protocol
이 요구한 사항들을 extension
에 분리하여 작성해주면 가독성을 높일 수 있습니다.
아래는 UIViewController
를 활용하여 TableView
를 구현한 사례를 나타낸 것입니다. 아시다시피 TableView
를 구현하기 위해 UITableViewDelegate
와 UITableViewDataSource
프로토콜을 채택하여 요구하는 메서드들을 구현해야하죠. 아래 코드 또한 첫 final class
는 UIViewController
를 상속 받아 사용하는 내용만, 따라오는 두 개의 extension
에서는 각각 UITableViewDataSource
와 UITableViewDelegate
내용을 구현하고 있습니다.
import UIKit
import OSLog
final class ArtworksTableViewController: UIViewController {
(... 불필요 부분 생략 ...)
}
// MARK: - Table view data source
extension ArtworksTableViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return artworks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ArtworkTableViewCell = tableView.dequeueReusableCell(
withIdentifier: Identifier.Cell.artwork,
for: indexPath
) as! ArtworkTableViewCell
cell.thumbnailImageView.image = UIImage(named: artworks[indexPath.row].imageName)
cell.titleLabel.text = artworks[indexPath.row].name
cell.shortDescriptionLabel.text = artworks[indexPath.row].shortDescription
return cell
}
}
// MARK: - Table view delegate
extension ArtworksTableViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
}
}
해당 내용은 Raywenderlich Swift Style Guide에서도 권장하고 있는 사항이니 잘 지켜주면 좋을 것 같네요.