✅ NSCache와 딕셔너리로 캐시를 구성했을때의 차이
1️⃣ NSCache
- 캐시(cache)는 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다.
- 캐시에 데이터를 미리 복사해 놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터에 접근할 수 있다.
memory cache
- iOS에서는 NSCache를 통해 메모리 캐시를 관리
- 앱이 종료되면 저장된 데이터도 날아간다.
disk cache
- FileManager객체를 사용하여 데이터를 파일 형태로 디스크에 저장
- UserDefaults, CoreData를 사용기도 함
- 앱이 종료되도 데이터가 남는다.
NSCache란?
- key-value쌍을 임시로 저장하는데 사용되는 변경 가능한 Collection
- NSCache는 자체적으로 시스템 메모리를 너무 많이 사용하지 않도록 자동으로 제거됨
- NSCache는 내부적으로 연결 리스트와 Dictionary를 사용
- NSCacheKey는 값을 복사하지 않고 참조
2️⃣ NSCache와 딕셔너리에서 캐시 구성의 차이
메모리 관리
- NSCache: 자동으로 메모리 관리. 메모리가 부족할 때, NSCache는 자동으로 일부 항목을 제거.
- Dictionary: 메모리가 부족할 때 자동으로 제거하지 않음. 따라서, 딕셔너리가 너무 큰 데이터를 유지하면 메모리 부족 문제가 발생
스레드 안전성
- NSCache: 스레드 안전, 여러 스레드에서 동시에 액세스하더라도 안전하게 동작.
- Dictionary: 기본적으로 스레드 안전 X. 여러 스레드에서 동시에 액세스하려면 동기화 메커니즘이 필요.
키의 형태
- NSCache: 키로 사용되는 객체는 NSObject를 준수. 따라서 키로 사용할 수 있는 객체의 형태가 제한.
Dictionary: 다양한 형태의 키를 사용.
가변성:
- NSCache: 가변적. 데이터를 캐시에 추가하거나 제거하는 것이 쉬움.
- Dictionary: Dictionary의 경우 가변성은 선택적. Dictionary는 불변이지만 NSMutableDictionary는 가변적
캐시 정책
- NSCache: 최대 캐시 크기나 객체 수를 지정하는 것과 같은 추가적인 캐시 정책을 설정할 수 있음.
- Dictionary: 캐시 정책이 내장되어 있지 않음. 모든 정책을 수동으로 구현해야 함.
3️⃣ 결론
- 딕셔너리는 메모리가 부족하면 값을 삭제하는 코드를 작성해야 하지만 NSCache는 메모리가 자동으로 관리된다.
- NSCache는 Thread-safe하다. 데이터를 쓸때마다 lock을 해줄 필요가 없다.
- NSCahce는 key를 복사하지않고 유지한다. retain 카운트만 증가시킨다.
- 딕셔너리는 key를 복사한다.
✅ URLSession
- iOS에서 제공하는 HTTP를 이용한 네트워킹을 통해 데이터를 주고받을 수 있게 도와주는 API를 제공해주는 클래스
- Thread-Safty하기 때문에 어떤 스레드에서든 자유롭게 Session과 Task를 생성
- URLSessionDataTask : 데이터를 받는 작업 수행 시 사용. background 세션에 대한 지원 X.
- URLSessionUploadTask : 데이터 업로드 시 사용.
- URLSessionDownloadTask : 데이터 다운로드 시 사용.
let requestBody = try! JSONSerialization.data(withJSONObject: parameters, options: [])
guard let url = URL(string: url) else {
print("Error: cannot create URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = requestBody
let defaultSession = URLSession(configuration: .default)
defaultSession.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
guard error == nil else {
print("Error occur: error calling POST - \\(String(describing: error))")
return
}
guard let data = data, let response = response as? HTTPURLResponse, (200..<300) ~= response.statusCode else {
print("Error: HTTP request failed")
return
}
guard let output = try? JSONDecoder().decode(Model.self, from: data) else {
print("Error: JSON data parsing failed")
return
}
completionHandler(true, output.data)
}.resume()
✅ prepareForReuse
1️⃣ dequeueReusableCell
일반적으로 테이블뷰에서 셀을 리턴하는 함수에서 셀을 재사용하도록 습관적으로 아래와 같이 코드를 작성
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SearchTableViewCell", for: indexPath) as! SearchTableViewCell
return cell
}
🌟 dequeueReusableCell
지정된 재사용 식별자(indexPath)에 대해 재사용이 가능한 테이블 뷰 셀 객체를 반환하고 테이블뷰에 추가한다
🌟🌟 parameter
identifier : 재사용할 셀 객체를 식별하기 위한 문자열. 이 매개 변수는 nil이 아니어야함
indexPath : 셀의 위치를 특정하는 식별자. 항상 데이터 원본 객체에서 제공하는 index path를 지정. 이 메소드는 index path를 사용하여 테이블뷰에서 셀의 위치를 기반으로 추가 구성을 수행.
🌟🌟 리턴 타입
UITableViewCell : 재사용 식별자와 연관된 UITableViewCell. 항상 유효한 셀을 반환.
- 셀에 configure 되는 내용은 다르지만 셀 자체는 재사용되기 때문에 content 와 무관한 것들 예를들어 셀의 alpha , editing, selection sate 등까지 재사용하게 된다.
2️⃣ prepareForReuse
- tableview의 delegate가 셀을 재사용하도록 준비
override func prepareForReuse() {
super.prepareForReuse()
}

✅ dark mode 지원
1️⃣ UIKit에서 dark mode
Assets Catalog의 Color Set 사용
- Asset Catalog에 새 Color Set을 추가
- 각 Color Set에는 Light 및 Dark 모드에 대한 다른 색상 값을 지정
Interface Builder에서 색상 설정
- Interface Builder에서 UI 요소의 색상을 설정할 때, Asset Catalog에서 생성한 Color Set을 사용
code
label.textColor = UIColor(named: "darkModeColor")
interface style
if self.traitCollection.userInterfaceStyle == .dark {
} else {
}
2️⃣ SwiftUI에서 dark mode
@Environment(\.colorScheme) var colorScheme
Group {
if colorScheme == .dark {
} else {
}
}
Color(colorScheme == .dark ? .yellow : .blue)
✅ ViewController의 생명주기
loadView()
- 이 메서드는 뷰 컨트롤러가 프로그래밍 방식으로 그것의 뷰를 생성할 때 호출.
- 대부분의 경우, 스토리보드나 Nib 파일을 사용하여 뷰를 로드하기 때문에 이 메서드를 직접 오버라이드할 필요 X.
viewDidLoad()
- 뷰 계층구조가 메모리에 로드된 후 호출.
- 초기화 코드를 여기에 넣는 것이 일반적.
viewWillAppear(_:)
- 뷰가 화면에 표시되기 직전에 호출.
- 이 시점에서 UI 업데이트나 애니메이션을 준비하는 데 사용.
viewWillLayoutSubviews()
- 뷰가 서브뷰의 레이아웃을 변경하기 직전에 호출.
- 필요한 경우 레이아웃 변경을 위한 추가 작업.
viewDidLayoutSubviews()
- 서브뷰들이 새로운 레이아웃으로 배치된 후 호출.
viewDidAppear(_:)
- 뷰가 화면에 완전히 표시된 후 호출
- 여기에서 추가적인 애니메이션을 시작하거나 데이터를 가져오는 작업을 수행.
viewWillDisappear(_:)
- 뷰가 화면에서 사라지기 직전에 호출
- 여기에서 리소스 해제, 저장, 타이머 중지 등의 작업을 수행
viewDidDisappear(_:)
deinit
- 뷰 컨트롤러 인스턴스가 메모리에서 해제되기 전에 호출되는 소멸자. 추가적인 메모리 해제 작업.
✅ TableView, CollectionView.
1️⃣ 공통점
- 둘 다 cell 을 기반으로 여러 데이터를 표현할 수 있는 View
- UIScrollView를 상속받는 클래스
2️⃣ 차이점
TableView

- UITableView는 한 개의 열과, 여러개의 행을 가지기 때문에 정보를 ‘행'으로만 나열해서 보여준다.(1차원) 그래서 수직으로만 스크롤이 가능하다.
- Cell은 기본적으로 제공되는 스타일이 존재한다.
- 간단한 목록으로 데이터를 표현할 때 주로 사용된다.
datasource
: 테이블뷰가 테이블을 만들 때 필요한 정보를 제공하고, 테이블의 행의 추가/삭제/재정렬할 때 데이터 모델을 관리한다.
delegate
: 화면에 보이는 모습과 행동을 담당한다. ( 몇개의 행을 보여줄 건지, 특정 행 선택시 이벤트 등 )
collectionView

- 여러 데이터를 관리하고 커스텀 가능한 레이아웃을 사용해서 사용자에게 보여줄 수 있는 객체
- UICollectionView는 테이블뷰에서 가진 기능을 전부 가지고, 여러 열과 여러개의 행을 가질 수 있어서 수직/수평 스크롤이 모두 가능하다. (2차원)
- 기본적으로 제공되는 Cell의 스타일이 없다.
- 하나의 열에 여러개의 cell을 표현하는 방식이기 때문에 cell 모양은 row에 맞춰서 디자인해야 한다.
- 데이터 표현방식을 다양한 모습으로 커스터마이징이 가능하다.
- UICollectionViewDelegateFlowLayout을 통해 본인이 원하는 모양으로 레이아웃을 커스텀해서 사용 가능하다.
3️⃣ 결론
- 간단한 데이터만 보여줘도 된다면 테이블뷰만 사용해도 충분하고,셀의 모양을 커스텀하길 원하거나, 나중에 디자인의 수정 가능성이 높다면 collectionView를 사용하도록 하자
✅ Dynamic Library, Static Library
1️⃣ library
🌟🌟 Xcode Target의 일부로 빌드되지 않은 코드 및 데이터 조각을 정의한 것
2️⃣ Static lib

- Static library : .a
- static library 코드는 executable file에 복사됨
- static library가 executable file의 일부가 되기 때문에, 앱이 실행되면 앱 소스코드 + static library 코드를 포함하는 것들이 앱의 주소 공간에 로드됨
-
많은 static library를 앱에 link하면 큰 executable file이 생성됨.
executable file안에 static library 코드가 복사되기 때문!
-
큰 executable file은 느린 시작 시간(launch time) + 큰 메모리 공간을 가져감.
-
static libary가 업데이트 되면 클라이언트 앱은 개발자가 업데이트 된 library와 다시 Link하지 않는 이상 업데이트 된 기능을 사용할 수 없음.
3️⃣ Dynamic lib

- Dynamic library : .dylib
- linker에 의해 만들어진 executable file에 library 코드가 모두 포함됐던 static과 달리, Dynamic library에 대한 참조만 executable file에 포함됨.
- executable file과 Link는 됐지만, 복사가 안됐다는 점이 가장 중요!!
- Dynamic library는 앱이 실행될 때 앱 주소 공간에 로드 됨
- Dynamic library를 로드 -> 그것이 Dynamic loader!!
✅ 오토레이아웃을 코드로 작성하는 방법
1️⃣ NSLayoutAnchor
myView.translatesAutoresizingMastIntoConstraints = false
let margins = view.layoutMarginsGuide
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).active = true
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).active = true
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0)
2️⃣ NSLayoutConstaint
NSLayoutConstraint(item: myView,
attribute: .leading,
relatedBy: .Equal,
toItem: view,
attribute: .leadingMargin,
multiplier: 1.0,
constant: 0.0).isActive = true
- 설명하고자 하는 레이아웃의 시각적인 표현을 제공하는 방식.
- 읽을 수 있도록 설계되어 있으며 뷰는 대괄호로 표시되고 뷰간의 연결은 하이픈(또는 뷰들을 떨어뜨리는 숫자에 의해 두개의 분리된 하이픈)을 사용
let views = ["myView": myView]
let formatString = "|-[myView]-|"
let constraints = NSLayoutConstraint.constraintsWithVisualFormat(formatString,
options: .AlignAllTop,
metrics: nil,
views: views)
NSLayoutConstraint.activateConstraints(constraints)
4️⃣ snapkit
pod 'SnapKit'
import SnapKit
let box = UIView()
self.view.addSubview(box)
box.snp.makeConstraints { (make) in
make.width.height.equalTo(50)
make.center.equalTo(self.view)
}
let anotherBox = UIView()
self.view.addSubview(anotherBox)
anotherBox.snp.makeConstraints { (make) in
make.top.equalTo(box.snp.bottom).offset(10)
make.left.equalTo(box).offset(20)
make.width.equalTo(box)
}
box.snp.updateConstraints { (make) in
make.width.equalTo(100)
}
✅ hugging, resistance
- 뷰에는 hugging과 resistance 2개의 우선순위가 있다.
- hugging은 최대 크기에 대한 제한, 주어진 크기보다 작아질 수 있다.
- resistance는 최소 크기에 대한 제한, 주어진 크기보다 커질 수 있다.
- 공간이 남게 되면 hugging이 높은 뷰는 자신의 크기를 유지하고, 낮은 뷰는 남은 공간만큼 뷰의 크기가 커지게 된다.
- 반대로 공간이 부족하게 되면 resistance가 높은 뷰는 자신의 크기를 유지하고, 낮은 뷰는 부족한 공간만큼 크기가 작아지게 된다.
1️⃣ hugging

- hugging priority 가 251로 같으면 같은 width를 유지한다.

- 빨간색의 hugging priority가 250이고, 파란색의 hugging priority가 251일 때 빨간색의 우선순위가 낮으므로 원래 크기를 유지하지 못하고 늘어난다.
2️⃣ Compression Resistance

- width의 constraint = 1000
- compression resistance = 1000
- 둘 다 우선순위가 같을 때 button의 width는 늘어나지 않음

- width의 constraint = 999
- compression resistance = 1000
- resistance의 우선 순위가 높아지면 넓이가 text에 맞게 늘어난다.
✅ Intrinsic Size
- Intrinsic Size: 콘텐츠 본질적인 크기
- Frame Size: 뷰의 크기

- UIView는 leading, trailing을 둘 다 설정해줘야 width를 동적으로 계산해서 에러가 나지 않음
- trailing을 삭제하면 에러가 남

- UILabel은 trailing을 주지 않더라도 에러가 나지 않음
- 이것이 바로, UILabel이 Intrinsic Content SIze를 가지기 때문!!
- UILabel은 Intrinsic Content Size 때문에 Width를 지정하지 않아도 에러가 나지 않는다는 의미

- 만약 label의 width = 100, height = 50으로 줬을 때
- 폰트 크기를 키우면
- Constraints에 의해 Width가 100, Height가 50으로 지정됐기 때문에, 그 크기를 넘길 시 글자들이 잘린다.
- Label은 자체적으로 본질적인 Width와 Height, 즉 Intrinsic Content Size를 가지기 때문!!!

- 그래서 UILabel에 leading, top만 줘도 에러가 나지 않고, 폰트 크기에 맞게 width, height가 동적으로 계산됨.
