[iOS] TIL

Zoe·2023년 10월 3일
0

iOS

목록 보기
22/39

✅ 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 : 데이터 다운로드 시 사용.
//example
// requestBody encoding
let requestBody = try! JSONSerialization.data(withJSONObject: parameters, options: [])

// URL
guard let url = URL(string: url) else {
    print("Error: cannot create URL")
    return
}

// Request : 헤더, 바디 추가
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = requestBody

// Session
let defaultSession = URLSession(configuration: .default)

// Task
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가 셀을 재사용하도록 준비
// tableViewCell.swift
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 {
    // Dark mode is active
} else {
    // Light mode is active
}

2️⃣ SwiftUI에서 dark mode

@Environment(\.colorScheme) var colorScheme

Group {
    if colorScheme == .dark {
        // Dark mode specific views
    } else {
        // Light mode specific views
    }
}

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의 일부로 빌드되지 않은 코드 및 데이터 조각을 정의한 것

  • 라이브러리와 앱의 소스코드 파일을 병합(merge)하는 프로세스를 "Link"

  • 라이브러리는 앱에 Link되는 방식에 따라 두가지 카테고리로 나뉨 -> static VS dynamic

2️⃣ Static lib

  • Static library : .a
  • static library 코드는 executable file에 복사됨
  • static library가 executable file의 일부가 되기 때문에, 앱이 실행되면 앱 소스코드 + static library 코드를 포함하는 것들이 앱의 주소 공간에 로드됨
  1. 많은 static library를 앱에 link하면 큰 executable file이 생성됨.
    executable file안에 static library 코드가 복사되기 때문!

  2. 큰 executable file은 느린 시작 시간(launch time) + 큰 메모리 공간을 가져감.

  3. 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

3️⃣ Visual Format Language

  • 설명하고자 하는 레이아웃의 시각적인 표현을 제공하는 방식.
  • 읽을 수 있도록 설계되어 있으며 뷰는 대괄호로 표시되고 뷰간의 연결은 하이픈(또는 뷰들을 떨어뜨리는 숫자에 의해 두개의 분리된 하이픈)을 사용
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가 동적으로 계산됨.

profile
iOS 개발자😺

0개의 댓글

관련 채용 정보