enum Nasa: String, CaseIterable {
** let baseURL = "https://apod.nasa.gov/apod/image/" // 불가능
static let baseURL = "https://apod.nasa.gov/apod/image/" // 가능
}
enum Nasa: String, CaseIterable {
var test: URL {
return URL(string: "https://www.naver.com")
}
}
/* 타입 저장 프로퍼티를 그냥 사용 */
enum Nasa: String, CaseIterable {
static let baseURL = "https://apod.nasa.gov/apod/image/"
// static 빼고 인스턴스 프로퍼티로 사용하려면 -> 에러 발생
static var photo: URL {
return URL(string: baseURL + self.allCases.randomElement()!.rawValue)!
}
}
/* 타입명을 통해 타입 프로퍼티 사용 */
enum Nasa: String, CaseIterable {
static let baseURL = "https://apod.nasa.gov/apod/image/"
var photo: URL {
return URL(string: Nasa.baseURL + self.allCases.randomElement()!.rawValue)!
}
}
동기 -> 하나의 작업이 끝날 때까지 다른 작업 시작할 수 없다
버튼을 누르면, 작업이 모두 끝날 때까지 버튼이 눌려있는 상태이다
순서가 맞춰지고, 끝나는 시점을 파악할 수 있다
@objc func syncButtonClicked() {
print("sync start")
downloadImage(imageView: firstImageView, value: "first")
downloadImage(imageView: secondImageView, value: "second")
downloadImage(imageView: thirdImageView, value: "third")
downloadImage(imageView: fourthImageView, value: "fourth")
print("sync end")
}
func downloadImage(imageView: UIImageView, value: String) {
print("===1===\(value)===")
let data = try! Data(contentsOf: Nasa.photo)
imageView.image = UIImage(data: data)
print("===2===\(value)===")
}
이미지가 화면에 보여지기 전까지 버튼이 눌린 상태 -> 다른 작업 불가
순서 o, 끝나는 시점 o
실행시키면 보라색 에러가 뜬다
비동기 -> 알바생들한테 일 뿌리고 작업 기다리지 않는다 -> 순식간에 프린트가 찍힌다
오래 걸리는 작업(let data = try! ~
)을 알바생한테 던진다
UI 관련 작업은 다시 main으로 가져온다
@objc func asyncButtonClicked() {
print("async start")
asyncDownloadImage(imageView: firstImageView, value: "first")
asyncDownloadImage(imageView: secondImageView, value: "second")
asyncDownloadImage(imageView: thirdImageView, value: "third")
asyncDownloadImage(imageView: fourthImageView, value: "fourth")
print("async end")
}
func asyncDownloadImage(imageView: UIImageView, value: String) {
print("===1===\(value)===", Thread.isMainThread)
DispatchQueue.global().async {
print("===2 - 1===\(value)===", Thread.isMainThread)
let data = try! Data(contentsOf: Nasa.photo)
DispatchQueue.main.async { // 이게 sync일 때와 async일 때의 차이는 뭘까?
imageView.image = UIImage(data: data)
print("===2 - 2===\(value)===", Thread.isMainThread)
}
print("===2- 3===\(value)===")
}
print("===3===\(value)===")
}
순서가 랜덤이고, 끝나는 시점 파악할 수 없다 (-> DispatchGroup)
1~10까지 그냥 출력, 11~20 main.sync로 출력
func serialSync() {
print("start")
for i in 1...10 {
sleep(1)
print(i, terminator: " ")
}
DispatchQueue.main.sync {
for i in 11...20 {
sleep(1)
print(i, terminator: " ")
}
}
print("end")
}
그래서 이거 잘 안쓴다
1~10을 main.async로 던져준다
결국 main이 일해야 하는 것이므로 작업 순서만 바뀐다
func serialAsync() {
print("start")
DispatchQueue.main.async {
for i in 1...10 {
sleep(1)
print(i, terminator: " ")
}
}
for i in 11...20 {
sleep(1)
print(i, terminator: " ")
}
print("end")
}
1~10을 알바생한테 던지긴 하는데, 결국 보낸 일이 다 끝나야 main이 일 할 수 있다
func globalSync() {
print("start")
DispatchQueue.global().sync {
for i in 1...10 {
sleep(1)
print(i, terminator: " ")
}
}
for i in 11...20 {
sleep(1)
print(i, terminator; " ")
}
print("end")
}
동시에 작업이 끝난다
작업이 빠르게 끝난다 라는게 중요하다 -> 약간 파이프라인 같은 느낌..?
네트워크 통신처럼 오래 걸릴 수 있는 작업들을 보통 global.async로 던진다
func globalAsync() {
print("start")
DispatchQueue.global().async {
for i in 1...10 {
sleep(1)
print(i, terminator: " ")
}
}
for i in 11...20 {
sleep(1)
print(i, terminator: " ")
}
print("end")
}
loop 안에서 도는 작업들을 각자 알바생한테 뿌린다
엄청나게 빨라진다
func globalAsync2() {
print("start")
for i in 100...300 {
DispatchQueue.global().async {
sleep(1)
print(i, terminator: " ")
}
}
for i in 1...20 {
sleep(1)
print(i, terminator: " ")
}
print("end")
}
작업 중인 쓰레드가 확 많아졌다가 확 줄어든다
비동기로 맡기는 코드가 동기이다
func dispatchGroup() {
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
for i in 1...10 {
print(i, terminator: " ")
}
}
DispatchQueue.global().async(group: group) {
for i in 11...20 {
print(i, terminator: " ")
}
}
DispatchQueue.global().async(group: group) {
for i in 21...30 {
print(i, terminator: " ")
}
}
DispatchQueue.global().async(group: group) {
for i in 31...40 {
print(i, terminator: " ")
}
}
// 첫 번째 매개변수 : 신호를 받을 때, 어떤 쓰레드로 받을지 (보통 .main)
// 두 번째 매개변수 : 클로저로 줌
group.notify(queue: .main) {
print("end")
}
}
예상대로 끝나는 시점을 확인할 수 있다
비동기로 맡기는 코드가 또 비동기이다
// callRequestRecommendation(_ movieId: Int) : 추천 영화 정보 api 호출 함수
// -> 비동기
func dispatchGroupNotify() {
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
self.callRequestRecommendation(872585) { value in
self.list0 = value
print("0")
}
}
DispatchQueue.global().async(group: group) {
self.callRequestRecommendation(976573) { value in
self.list1 = value
print("1")
}
}
DispatchQueue.global().async(group: group) {
self.callRequestRecommendation(76600) { value in
self.list2 = value
print("2")
}
}
DispatchQueue.global().async(group: group) {
self.callRequestRecommendation(346698) { value in
self.list3 = value
print("3")
}
}
group.notify(queue.main) {
print("hi")
self.posterCollectionView.reloadData()
}
}
group.notify
가 가장 먼저 실행된다.
따라서 reloadData()
가 실행되어도 화면에 보여줄 데이터가 없다
group.notify
가 실행되는 시점은 group
의 비동기 코드들이 모두 일을 끝냈을 때 이다group.notify
가 실행된다group.enter()
: RC += 1
group.leave()
: RC -= 1
group
에 대한 RC가 0이 되는 순간 notify
가 실행된다
func dispatchGroupEnter() {
let group = DispatchGroup()
group.enter()
self.callRequestRecommendation(872585) { value in
self.list0 = value
print("0")
group.leave()
}
group.enter()
self.callRequestRecommendation(976573) { value in
self.list1 = value
print("1")
group.leave()
}
group.enter()
self.callRequestRecommendation(76600) { value in
self.list2 = value
print("2")
group.leave()
}
group.enter()
self.callRequestRecommendation(346698) { value in
self.list3 = value
print("3")
group.leave()
}
group.notify(queue: .main) {
print("END")
self.posterCollectionView.reloadData()
}
}
함수 실행 전, group의 RC를 올리고, 함수가 실행되면 RC를 내린다
let json = """
{
"quote": "The will of man is his happiness",
"author": "Friedrich Schiller",
"category": "happiness"
}
"""
// Struct
struct Quote: Decodable { // 프로토콜 채택. 바깥에서 오는 데이터를 처리해줄게~ 라는 뜻
let quote: String
let author: String
let category: String
}
// String -> Data
guard let result = json.data(using: .utf8) else { fatalError("error") }
// Data -> Struct
do {
let value = try JSONDecoder().decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
let json = """
{
"quote": "The will of man is his happiness",
"author": "Friedrich Schiller",
"category": "happiness"
}
"""
// Struct
struct Quote: Decodable {
let quoteContent: String?
let author: String?
let category: String?
}
// String -> Data
guard let result = json.data(using: .utf8) else { fatalError("error") }
// Data -> Struct
do {
let value = try JSONDecoder().decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
nil
로 저장된다let json = """
{
"quote_content": "The will of man is his happiness.",
"author_name": "Friedrich Schiller",
}
"""
// Struct
struct Quote: Decodable {
let quoteContent: String
let authorName: String
}
// String -> Data
guard let result = json.data(using: .utf8) else { fatalError("error") }
// Data -> Struct
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let value = try decoder.decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
let json = """
{
"quote_content": "The will of man is his happiness.",
"author_name": "Friedrich Schiller",
"likelike": 34567
}
"""
// Struct - CodingKeys
struct Quote: Decodable {
let content: String
let authorName: String
let like: Int
// 이걸 사용하지 않으면 JSON key와 변수명이 동일해야 한다
// 정해져 있는 틀이기 때문에 스펠링 조심해야 함
enum CodingKeys: String, CodingKey {
case content = "quote_content"
case authorName = "author_name"
case like = "likelike"
}
}
// String -> Data
guard let result = json.data(using: .utf8) else { fatalError("error") }
// Data -> Struct
let decoder = JSONDecoder()
do {
let value = try decoder.decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
enum CodingKeys
를 써줘서 프로퍼티와 키를 맞춰준다let json = """
{
"quote_content": "The will of man is his happiness.",
"author_name": null,
"likelike": 34567
}
"""
// Struct
struct Quote: Decodable {
let content: String
let authorName: String
let like: Int
let isInfluencer: Bool // 좋아요 3만개 이상이면 true
enum CodingKeys: String, CodingKey {
case content = "quote_content"
case authorName = "author_name"
case like = "likelike"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keydBy: CodingKeys.self)
content = try container.decode(String.self, forKey: .content)
authorName = (try? container.decodeIfPresent(String.self, forKey: .authorName)) ?? "unknown"
like = try container.decode(Int.self, forKey: .like)
isInfluencer = (30000...).contains(like) ? true : false
}
}
// String -> Data
guard let result = json.data(using: .utf8) else { fatalError("error") }
// Data -> Struct
let decoder = JSONDecoder()
do {
let value = try decoder.decode(Quote.self, from: result)
print(value)
} catch {
print(error)
}
var movieList: [Movie] = [] // 이거 Movie 구조체도 직접 만들어야 해
func callReqeust(_ query: String) {
let txt = ~~
let url = ~~
AF.request(url, method: .get)
.validate(statusCode: 200...500)
.responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print(json)
let statusCode = response.response?.statusCode ?? 500
if (statusCode == 200) {
let title = json[""][""][""]
// 이러면서 위치 찾고~
// 배열에다 append하고~
}
case .failure(let error)
print(error)
}
}
}
var result: BoxOffice? // 퀵타입이 만들어준 구조체😆
func callRequest(_ query: String) {
let txt = ~~
let url = ~~
AF.request(url, method: .get)
.validate(statusCode: 200...500)
.reponseDecodable(of: BoxOffice.self) { response in
let statusCode = response.response?.statusCode ?? 500
if (statusCode == 200) {
guard let value = response.value else { return }
self.result = value
// 배열이 있어도 append 할 필요 없이
// 그냥 통으로 넣어버려
//completionHandler(value)
}
else {
print("Error! statusCode : \(statusCode)")
print(response)
}
}
}
즐겁게 읽었습니다. 유용한 정보 감사합니다.