TIL

31일차 TIL

h_luz 2024. 4. 9. 20:32
Swift 알고리즘 및 언어공부

 

 

func solution(_ cards1: [String], _ cards2: [String], _ goal: [String]) -> String {
    var i = 0
    var j = 0
    var cardOne = cards1
    var cardTwo = cards2
    var result = true

    for str in goal {
        if i < cardOne.count && cardOne[i] == str {
            i += 1
        } else if j < cardTwo.count && cardTwo[j] == str {
            j += 1
        } else {
            result = false
            break
        }
    }
    return result ? "Yes" : "No"
}

 

하 또 엄청.. 어렵네...... 왜 이렇게 뒤로 갈수록 문제가 길어지는가.. (코드도 길어짐..)

처음에 문제 이해하는 것부터 어려웠다.. 국어실력에도 문제가 있는 건가..

 

goal이랑 card에 내용이 같은 지 비교하는 간단한 문제인데, 여기서 중요했던 점은 순서였던 것 같다.

 

for문 써서 돌리려다가 card1과 card2의 길이가 다르니까.. 범위를 어떻게 정해야 할지 몰라서 곤란했다..

결국 if문 두 개로 나눠주기로 함

 

처음에는 removeFirst 사용해서 막 지우면서 비교하다가.. 굳이..? 이런 생각이 들어서 그냥 이렇게 해주었는 데 성공했음

아마 removeFirst 안 썼으니까 cardOne 이랑 cardTwo도 만들어 줄 필요 없었을 듯? ㅋㅋ큐ㅠ 

 

/최종코드
func solution(_ cards1: [String], _ cards2: [String], _ goal: [String]) -> String {
    var i = 0
    var j = 0
    var result = true

    for str in goal {
        if i < cards1.count && cards1[i] == str {
            i += 1
        } else if j < cards2.count && cards2[j] == str {
            j += 1
        } else {
            result = false
            break
        }
    }
    return result ? "Yes" : "No"
}

solution(["i","water","drink"], ["want","to"], ["i","want","to","drink","water"])
// No 출력
solution(["i","drink","water"], ["want","to"], ["i","want","to","drink","water"])
// Yes 출력

 

언제쯤이면 알고리즘을 슥슥 풀어낼 수 있을까..

넘 머리 아픔.. ㅠㅠㅜ

 

//다른사람의풀이

import Foundation

func solution(_ cards1:[String], _ cards2:[String], _ goal:[String]) -> String {
    var (ret, c1, c2) = ("Yes", cards1, cards2)
    goal.forEach {
        if c1.first == $0 { c1.removeFirst() }
        else if c2.first == $0 { c2.removeFirst() }
        else { ret = "No"}
    }
    return ret
}

오 이분은 removeFirst로 푸셔서 신기해서 가져왔다

음.. forEach가 뭐지..

 

forEach(_:)

: for-in 루프와 동일한 순서로 시퀀스의 각 요소에 대해 지정된 클로저를 호출

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


numberWords.forEach { word in
    print(word)
}

 

for-in루프와 다른 점

1. break 또는 continue문을 사용하여 클로저의 현재 호출을 종료하거나 후속 호출을 건너뛸 수 없다.

2. return 클로저에서 사용하면 외부 범위가 아닌 현재 호출에서만 종료, 후속 호출을 건너뛰지 않는다.

 

앗 뭔가.. 저번에 프로젝트할 때 썼었던 거 같다.... ㅋㅋㅋㅋ 클로저를 사용하기 위한 for문인 건가..?

출처는 공식문서

 

 


 

강의 정리  (스파르타 코딩클럽 강의를 듣고 정리한 것)

 

 

Swift 메모리 구조 (4가지 영역)

 

Code 영역

: 프로그램에 실행 코드 저장공간 (Read-Only)

  • 컴파일된 소스코드 프로그램 명령어가 들어있다.

Data 영역

: 변수 상수가 저장되어 있는 저장공간

  • 프로그램 시작 시 할당되어 종료까지 유지

Heap 영역

: 프로그램 실행 중 동적으로 할당된 데이터가 저장되어 있는 저장공간

  • 런타임 중 메모리할당이 필요한 경우 사용된다
  • 개발자가 제어가능 (직접적으로 포인터를 통해 접근)
  • 메모리의 자유공간을 사용 (크기, 생존기간에 대한 동적 요구에 대응)
  • 참조타입(reference type) 저장

Stack 영역

: 함수 호출과 관련된 정보가 저장되어 있는 저장공간

  • 컴파일 타임(기계어로 변환되는 시점)에 크기 결정
  • 함수가 호출될 때마다 필요한 기타 정보(로컬변수, 함수매개변수, 반환주소) 저장
  • 함수 실행 종료 시 관련 데이터는 제거된다.
  • 값 타입(value type) 저장
  • 후입선출 (LIFO)

 

ARC (자동 메모리 할당 해제)

: Automatic Reference Counting 

  • Heap 영역의 객체에 대한 강한 참조(strong reference) count를 추적해서 객체가 필요 없을 때(=count가 없을 때) 해당 객체에 대한 메모리를 자동 해제하는 기능
  • 두 객체가 서로 강하게 참조하는 경우, 순환참조 발생
    • 순환참조: 사용되지 않는 두 객체가 서로 강하게 참조할 때, count 감소가 안 되어서 메모리에서 해제되지 못하고, 누수됨
      • 해결 방법으로는 약한 참조 미소유 참조가 있다.
        • 약한 참조 : 변수 선언 시 앞에 weak 붙이기 ( 참조 count 증가x )
        • 미소유 참조 : 변수 선언 시 앞에 unowned 붙이기 ( Optional값 x, 객체가 이미 해제되지 않았음을 확신할 수 있을 때 사용 )

* 약한 참조 예시

class Person {
	var name: String
    weak var friend: Person? //약한 참조
    init(name:String) {
    	self.name = name
    }
}

var person1: Person? = Person(name:"Alice")
var person2: Person? = Person(name:"Bob")
person1?.friend = person2
person2?.friend = person1

//person1이 nil로 할당되어 메모리에서 해제됨, 그에 따라 person1과 연결된 모든 객체의 참조 카운트 감소
person1 = nil

 

* 미소유 참조 예시

class Country {
	var name: String
    var capital: City!
    init(name: String, capitalName: String) {
    	self.name = name
        self.capital = City(name: capitalName, country: self)
    }
}

class City {
	var name: String
    unowned var country: Country //미소유 참조
    init(name:String, country:Country) {
    	self.name = name
        self.country = country
    }
}
// Country가 없는데 City가 있을 수 없다.
// City 사용 시점에서 Country가 해제되지 않는다고 확신할 때 사용

 


 

URL 구성요소

Protocol : 클라이언트와 서버간에 통신방법을 지정( http -> 보안이 없는 통신 / https -> 보안된 통신 )

Domain : 호스팅되는 서버의 주소

Port : 서버에서 API 요청을 수신하는 데 사용되는 포트 ( http -> 80 https -> 433 / 생략 가능 )

Path: 서버에서 요청된 자원이나 서비스의 위치(경로)

Query Parameters : URL에 추가정보를 전달하는 데 사용

 

REST ( Representational State Transfer )

: http 프로토콜을 기반으로 클라이언트와 서버 간의 통신 규칙 정의 ( * 모든 데이터는 자원으로 표현됨 )

  • HTTP Method
    • GET : 자원을 읽기 위한 메소드 (유저 조회)
    • POST : 새로운 자원 생성 (신규 유저 가입) , 역등성을 가지지 않음 ( = 여러번 같은 요청을 해도 모두 다른 자원으로 결과값 )
    • PUT : 기존 자원을 업데이트 (기존 유저 정보 수정) , 역등성을 가짐
    • DELETE : 기존 자원 삭제 ( 기존 유저 삭제 )
    • 그 외에도 다양한 메소드 존재
  • URI(Uniform Resource Identifier) : 모든 자원은 고유한 식별자를 지님. URI는 자원을 찾을 수 있는 주소 (리소스 식별)
    * URI와 URL의 차이

 

URLSession

: 네트워크 데이터를 가져오거나 보내는 작업

  • 비동기적 네트워킹(Asynchronous Networking) : 네트워크 작업 백그라운드에서 수행( 앱 성능⬆️, 응답성 유지⬆️ )
    - 만약 백그라운드에서 수행되지 않으면 네트워크가 요청되는 동안 화면이 멈추고, 다른 작업을 할 수 없게 됨
  • 다양한 데이터 전송 방식 지원 (Json, 이미지, 파일 등등)
  • 캐시와 쿠키 관리 : 네트워크 응답을 캐싱하고, 쿠키를 관리할 수 있는 기능 제공
//URLSession으로 GET 요청
//URLSession 인스턴스 생성
let session = URLSession.shared

//URL 생성
if let url = URL(string: "https://api.example.com/data") {
    //URLSessionDataTask를 사용하여 비동기적으로 데이터 요청
    let task = session.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print("Error: \(error)")
        }else if let data = data {
            // 데이터를 받아온 후 처리하는 로직
            print("Received data: \(data)")
        }
    }
    //네트워크 요청 시작
    task.resume()
}
//response는 코드 응답에 대한 검증 코드에 대한 내용이 들어갈 수 있음

 

//POST, 위에 방식대로 하면 GET이 기본이다.

let session2 = URLSession.shared
if let url2 = URL(string: "https://api.example.com/data") {
    //비동기적 데이터 요청에서 다름
    //URL Request 생성
    var request = URLRequest(url: url2)
    //HTTP 메서드 설정(POST일 경우)
    request.httpMethod = "POST" //GET,PUT,DELETE 사용 가능
    //HTTP Body에 보낼 데이터 설정(JSON 형식)
    let parameters: [String:String] = [
        "value1":"example value",
        "value2":"example value"
        //추가적인 필요 데이터 추가 가능
    ]
    //JSON 데이터 설정
    request.httpBody = try? JSONEncoder().encode(parameters)
    
    //위와 같은 코드
    let task2 = session2.dataTask(with: url2) { (data, response, error) in
        if let error = error {
            print("Error: \(error)")
        }else if let data = data {
            // 데이터를 받아온 후 처리하는 로직
            print("Received data: \(data)")
        }
    }
    //네트워크 요청 시작
    task2.resume()
}

 

Decodable 프로토콜

: 데이터를 객체로 디코딩할 때 사용 (즉, 외부 데이터(JSON)를 Swift 데이터 모델로 변환하는데 필요한 프로토콜)

//JSON
{
    "key" : 1,
    "name" : "John Doe"
}

// Decodable
struct User: Decodable {
    let id:Int
    let name:String
    
    public enum CodingKeys: String, CodingKey {
    	case id = "key"
        case name
    }
    //Decoding
    init(from decoder: Decoder) throws {
    	let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
    }
}

 

Encodable 프로토콜

: Decodable과 반대 개념, 객체를 데이터로 인코딩할 때 사용 (즉, Swift 데이터 모델을 JSON으로 변환)

//Swift Encodable
struct User: Encodable {
    let id:Int
    let name:String
    
    public enum CodingKeys: String, CodingKey {
    	case id = "key"
        case name
    }
    
    //Encoding 로직
    func encode(to encoder: Encoder) throws {
    	var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
    }
}

//JSON
{
    "key" : 1,
    "name" : "John Doe"
}

 

Codable 프로토콜

: 두 가지 하위 프로토콜인 Decodabel과 Encodable의 결합 (즉, 두 가지 변환 방법이 모두 필요할 때 사용)

//Codable
struct User: Codable {
    let id:Int
    let name:String
    
    public enum CodingKeys: String, CodingKey {
    	case id = "key"
        case name
    }
    
    //Encoding 로직
    func encode(to encoder: Encoder) throws {
    	var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
    }
    //Decoding
    init(from decoder: Decoder) throws {
    	let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        name = try container.decode(String.self, forKey: .name)
    }
}

 

JSON -> Model 변환

데이터를 모델 객체로 디코딩 (실패할 가능성이 있으니 do-catch문 사용)

do {
    let user = try JSONDecoder().decode(User.self, from:data)
    print("디코딩된 사용자: \(user)")
} catch {
	print("디코딩 에러: \(error)")
}

 


실습 문제

JSON Dummy API 를 활용해 상품 정보를 가져와 출력

//Decodable
struct Product: Decodable {
    let id: Int
    let title: String
    let description: String
    let price: Double
    let discountPercentage: Double
    let rating: Double
    let stock: Int
    let brand: String
    let category: String
    let thumbnail: String
    let images: [String]
}

let productID = 2

//URL 생성
if let url = URL(string: "https://dummyjson.com/products/\(productID)") {
    //URLSessionDataTask를 사용하여 비동기적으로 데이터 요청
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        if let error = error {
            print("Error: \(error)")
        }else if let data = data {
            do {
                let product = try JSONDecoder().decode(Product.self, from:data)
                print("디코딩된 제품: \(product)")
            } catch {
                print("디코딩 에러: \(error)")
            }
        }
    }
    //네트워크 요청 시작
    task.resume()
}
//response는 코드 응답에 대한 검증 코드에 대한 내용이 들어갈 수 있음

 


 

인터넷이 계속 안 되어서.. 손 필기해놨던 거 겨우 옮겨적었다 !
뭔가 알 것 같으면서 모르겠는 네트워크 통신..
직접 사용해보면서 익숙해지는 게 좋을 것 같다!
내일은 강의 끝내고, 내일모레부터는 과제 들어가면 좋을 것 같다 !

'TIL' 카테고리의 다른 글

33일차 TIL  (4) 2024.04.12
32일차 TIL  (4) 2024.04.11
30일차 TIL  (1) 2024.04.08
29일차 TIL  (2) 2024.04.05
28일차 TIL  (1) 2024.04.04