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, 객체가 이미 해제되지 않았음을 확신할 수 있을 때 사용 )
- 해결 방법으로는 약한 참조와 미소유 참조가 있다.
- 순환참조: 사용되지 않는 두 객체가 서로 강하게 참조할 때, count 감소가 안 되어서 메모리에서 해제되지 못하고, 누수됨
* 약한 참조 예시
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는 코드 응답에 대한 검증 코드에 대한 내용이 들어갈 수 있음
인터넷이 계속 안 되어서.. 손 필기해놨던 거 겨우 옮겨적었다 !
뭔가 알 것 같으면서 모르겠는 네트워크 통신..
직접 사용해보면서 익숙해지는 게 좋을 것 같다!
내일은 강의 끝내고, 내일모레부터는 과제 들어가면 좋을 것 같다 !