https://www.acmicpc.net/problem/2294

 

2294번: 동전 2

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다. 가치가 같은 동전이 여러 번 주

www.acmicpc.net

직전에 풀었던 15988번 문제와 비슷하면서도 고민을 해봐야 하는 문제였다. 결국 다른 분의 풀이를 보고서 정답을 얻을 수 있었다.

 

풀이

배열 dp는 k개의 요소로 구성된 배열이며 dp[k]는 k원을 만들 수 있는 동전의 개수를 담고 있다.

dp[0]은 0원을 만들 수 있는 동전의 개수. 즉 0개부터 시작한다. 이후 동전을 담아놓은 배열을 순회하면서 dp를 갱신한다.

예제를 예시로 들겠다. 1원 5원 12원 동전이 있고, 목표 액수는 15원이다. 

우선 1원만 사용하여 dp배열을 갱신하면 아래와 같다.

0 0 0개
1 1개
2 1 + 1 2개
3 1 + 1 + 1 3개
4 1 + 1 + 1 + 1 4개
5 1 + 1 + 1 + 1 + 1 5개
... ... ...
15 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1  15개

다음으로 5원을 사용하여 dp배열을 갱신하면 다음과 같다.

0 0 0개
... ... ...
5 5 1개
6 1 + 5 2개
7 1 + 1 + 5 3개
8 1 + 1 + 1 + 5 4개
9 1 + 1 + 1 + 1 + 5 5개
10 5 + 5 2개
11 1 + 5 + 5 3개

사용할 동전의 가치가 coin원이고, 목표금액이 k원이라면 dp 배열의 요소 dp[k]는 dp[k-coin]+1로 갱신이 가능하며, 이를 통해 더 작은 값으로 갱신해주면 된다. 

즉 coin번째 배열부터 시작하여 갱신을 시작하면 되며, dp[0] = 0 인 상태에서 점화식은 다음과 같이 세울 수 있다.

점화식

dp[k] = min( dp[k], dp[k-coin]+1 )

 

코드

import Foundation

let nk = readLine()!.split(separator: " ").map{Int($0)!}
let n = nk[0]
var k = nk[1]

var dp = Array(repeating:10001, count:10001)
dp[0] = 0
var coins = [Int]()
for _ in 0..<n{
    let coin = Int(readLine()!)!
    if coin <= 10000{ coins.append(coin) }
}
//coins = coins.sorted(by: <)

for coin in coins{
    for i in coin...10000{
        dp[i] = min(dp[i], dp[i-coin]+1)
    }
}
if dp[k] == 10001{
    print(-1)
}else{
    print(dp[k])
}

문제를 풀 당시 coins배열을 오름차순 정렬했는데, 풀이를 적다 보니 coin의 오름차순 정렬은 할 필요가 없다는 것을 깨달았다.

'Problem Solving > BOJ' 카테고리의 다른 글

[1600] 말이 되고픈 원숭이  (2) 2022.10.05
[4179] 불!  (0) 2022.09.29
[15988] 1, 2, 3 더하기 3  (0) 2022.09.20
[1890] 점프  (0) 2022.09.17
[6588] 골드바흐의 추측  (0) 2022.09.16

https://www.acmicpc.net/problem/15988

 

15988번: 1, 2, 3 더하기 3

각 테스트 케이스마다, n을 1, 2, 3의 합으로 나타내는 방법의 수를 1,000,000,009로 나눈 나머지를 출력한다.

www.acmicpc.net

대표적인 동적 계획법 문제이다. 동적 계획법은 항상 마주할 때마다 느끼듯이 점화식을 고민하는 데에 너무 오랜 시간이 걸린다. 더 많은 문제들을 풀어봐야 하나 보다.

 

풀이

정답을 표현할 배열 dp는 n+1개의 요소를 가지는 배열이며, dp[n]은 "1,2,3을 사용하여 n을 만들 수 있는 식의 개수"라고 정의한다.

점화식을 사용하기 위해서 배열에 최소 3개의 요소가 담겨있어야 한다. 1부터 3까지의 경우의 수를 적어보면 다음과 같다.

 

n 식의 종류
0 표현 불가능(입력범위 아님)
1 1
2 1+1, 2
3 1+1+1, 2+1, 1+2, 3

이후 1,2,3을 이용해 4를 만드는 식을 만든다고 가정할 경우, 다음과 같은 생각을 해볼 수 있다.

  1. 1을 만들 수 있는 모든 식에 "+3"을 추가한다면 4를 만들 수 있다.
  2. 2를 만들 수 있는 모든 식에 "+2"를 추가한다면 4를 만들 수 있다.
  3. 3을 만들 수 있는 모든 식에 "+1"을 추가한다면 4를 만들 수 있다.

따라서 다음과 같은 점화식을 세울수있게된다.

dp[n] = dp[n-1] + dp[n-2] + dp[n-3]

다만 문제를 보면 알 수 있듯이 n의 크기가 1,000,000이므로 정답을 출력할 때 1,000,000,009로 나눈 나머지를 출력하라고 적혀있다.

모듈러 산술 연산법칙은 분배 법칙이 성립하므로 코드를 적을 때는 다음과 같이 적는다.

 

dp[n] = ((dp[n-1]%1,000,000,009) + (dp[n-2]%1,000,000,009) + (dp[n-3]%1,000,000,009))%1,000,000,009

 

정답 코드

import Foundation

var dp = Array(repeating: 0, count: 1000001)
dp[1] = 1
dp[2] = 2
dp[3] = 4
let mod = 1000000009

for i in 4...1000000{
    dp[i] = ((dp[i-1]%mod) + (dp[i-2]%mod) + (dp[i-3]%mod))%mod
}

for _ in 0..<Int(readLine()!)!{
    let n = Int(readLine()!)!
    print(dp[n])
}

'Problem Solving > BOJ' 카테고리의 다른 글

[4179] 불!  (0) 2022.09.29
[2294] 동전 2  (0) 2022.09.22
[1890] 점프  (0) 2022.09.17
[6588] 골드바흐의 추측  (0) 2022.09.16
[1158] 요세푸스 문제  (0) 2022.01.20

https://www.acmicpc.net/problem/1890

 

1890번: 점프

첫째 줄에 게임 판의 크기 N (4 ≤ N ≤ 100)이 주어진다. 그 다음 N개 줄에는 각 칸에 적혀져 있는 수가 N개씩 주어진다. 칸에 적혀있는 수는 0보다 크거나 같고, 9보다 작거나 같은 정수이며, 가장

www.acmicpc.net

풀이

동적 계획법 문제이다. 처음에는 깊이 우선 탐색으로 시도했으나 시간 초과가 발생. 동적 계획법 문제로 분류되어있는 걸 확인하고 난 후 고민했으나, 결국 다른 사람의 풀이과정을 이해하고 코드를 다시 작성하였다.

 

시간 초과 코드

import Foundation

let n = Int(readLine()!)!

var map = Array(repeating: [Int](), count: n)
for i in 0..<n{
    map[i] = readLine()!.split(separator: " ").map{Int($0)!}
}
var ans = 0

func dfs(x:Int, y:Int){
    if map[x][y]==0{
        if x==n-1 && y==n-1{
            ans += 1
        }
        return
    }
    let length = map[x][y]
    if x+length<n{
        dfs(x: x+length, y: y)
    }
    if y+length<n{
        dfs(x: x, y: y+length)
    }
}
dfs(x: 0, y: 0)
print(ans)

 

해결 코드

import Foundation

let n = Int(readLine()!)!

var map = Array(repeating: [Int](), count: n)
var dp = Array(repeating: Array(repeating: 0, count: n), count: n)
for i in 0..<n{
    map[i] = readLine()!.split(separator: " ").map{Int($0)!}
}

dp[0][0]=1
for x in 0..<n{
    for y in 0..<n{
        if map[x][y]==0 || dp[x][y]==0{continue}
        let nx = x + map[x][y]
        let ny = y + map[x][y]
        if nx<n{ dp[nx][y] += dp[x][y]}
        if ny<n{ dp[x][ny] += dp[x][y]}
    }
}
print(dp[n-1][n-1])

'Problem Solving > BOJ' 카테고리의 다른 글

[2294] 동전 2  (0) 2022.09.22
[15988] 1, 2, 3 더하기 3  (0) 2022.09.20
[6588] 골드바흐의 추측  (0) 2022.09.16
[1158] 요세푸스 문제  (0) 2022.01.20
[1021] 회전하는 큐  (0) 2022.01.19

https://www.acmicpc.net/problem/6588

 

6588번: 골드바흐의 추측

각 테스트 케이스에 대해서, n = a + b 형태로 출력한다. 이때, a와 b는 홀수 소수이다. 숫자와 연산자는 공백 하나로 구분되어져 있다. 만약, n을 만들 수 있는 방법이 여러 가지라면, b-a가 가장 큰

www.acmicpc.net

풀이

소수 판별 문제이다. 문제를 보고서 소수 배열과 투 포인터를 사용하여 풀이를 제출했으나 시간 초과가 나왔다.

문제를 해결하기 위해서는 소수 배열의 작은 숫자부터 n의 차를 구한 수를 소수인지 판별하는 방법으로 해결해야 한다는 게시글을 보고서 다시 제출하였더니 통과했다.

 

시간 초과 코드

import Foundation

var numbers = Array(repeating: true, count: 1000001)
var primes = [Int]()
for i in 0..<1000001{
    if i <= 1{
        numbers[i] = false
        continue
    }
    if numbers[i]{
        if i != 2{
            primes.append(i)
        }
        for k in stride(from: i+i, to: 1000001, by: +i){
            numbers[k] = false
        }
    }
}

while let input = readLine(){
    let n = Int(input)!
    if n == 0{ break }
    
    var flag = false
    var start = 0
    var end = primes.count-1
    while start < end{
        let sum = primes[start] + primes[end]
        if sum > n{
            end -= 1
            continue
        }else if sum == n{
            flag = true
            break
        }else{
            start += 1
        }
    }
    if flag{
        print(n,"=",primes[start],"+",primes[end])
    }else{
        print("Goldbach's conjecture is wrong.")
    }
}

 

해결 코드

import Foundation

var primes = Array(repeating: true, count: 1000001)

for i in 0..<1000001{
    if i <= 1{
        primes[i] = false
        continue
    }
    if primes[i]{
        for k in stride(from: i+i, to: 1000001, by: +i){
            primes[k] = false
        }
    }
}

while let input = readLine(){
    let n = Int(input)!
    if n == 0{ break }
    
    var flag = false
    for i in 3..<1000001{
        if primes[i]{
            let chk = n-i
            if primes[chk]{
                flag = true
                print(n,"=",i,"+",chk)
                break
            }
        }
    }
    if !flag{
        print("Goldbach's conjecture is wrong.")
    }
}

 

심지어 문제를 풀고나서 알아낸 점은 같은 소수의 구성으로도 n을 만들 수 있다면 정답이 될 수 있다는 것이다. 투 포인터를 사용한 코드를 적을 때에 당연히 서로 다른 소수라는 가정을 한 채로 작성했었다. 대표적인 예시가 6을 입력할 경우 투 포인터를 사용한 코드는 불가능한 조합이라는 결과를 출력하지만, 통과했던 코드는 3 + 3 조합을 출력하게 된다.

'Problem Solving > BOJ' 카테고리의 다른 글

[2294] 동전 2  (0) 2022.09.22
[15988] 1, 2, 3 더하기 3  (0) 2022.09.20
[1890] 점프  (0) 2022.09.17
[1158] 요세푸스 문제  (0) 2022.01.20
[1021] 회전하는 큐  (0) 2022.01.19

 

 

observeOn(_ scheduler: ImmediateSchedulerType )

rxSwift의 scheduler는 stream 속의 연산들을 어느 스레드에서 동작하게 해 줄지 rxSwift에서 결정해주는 역할을 하고 있다.

observeOn() 오퍼레이터를 통해 결정해줄 수 있으며, 매개변수로 schedular를 입력받는다. observeOn() 오퍼레이터의 다음 연산부터 지정된 스레드로 연산을 수행한다.

@IBAction func exMap3() {
    Observable.just("800x600")
        .observeOn(ConcurrentDispatchQueueScheduler(qos: .default))    //ConcurrentDispatchQueueScheduler 동작
        .map { $0.replacingOccurrences(of: "x", with: "/") }    
        .map { "https://picsum.photos/\($0)/?random" }          
        .map { URL(string: $0) }                                
        .filter { $0 != nil }
        .map { $0! }                                            
        .map { try Data(contentsOf: $0) }                       
        .map { UIImage(data: $0) }                              
        .observeOn(MainScheduler.instance)    //MainScheduler 동작
        .subscribe(onNext: { image in
            self.imageView.image = image
        })
        .disposed(by: disposeBag)
}

url을 통해 이미지를 불러오는 함수다. 요청과 응답의 대기시간이 있기에, 스레드를 분리해서 작업해야 응답 대기시간 동안에 대기하는 동작을 막을 수 있다. 첫 번째 observeOn() 오퍼레이터를 통해서 concurrent 큐에서 요청 작업을 하게 된다. 응답에 대한 데이터를 UIImage 형태로 변환 후 이미지를 사용자에게 보여주기 위한 UI작업은 두 번째 observeOn( )을 통해 MainScheduler로 전환하여 작업하게 된다.


subscribeOn(_ scheduler: ImmediateSchedulerType )

observeOn() 오퍼레이터의 경우 Observable이 생성된 이후부터 적용할 수 있다. Observable이 생성되는 부분부터 특정 스레드를 지정하고 싶다면, subscribeOn() 오퍼레이터로 해결이 가능하다.

.subscribeOn(_ scheduler: ImmediateSchedulerType)
//Wraps the source sequence in order to run its subscription and unsubscription logic on the specified scheduler.
특정 스케줄러에서 구독 및 구독취소 로직을 실행하기 위해 소스 시퀀스(스트림)를 감쌉니다.

 

 

설명을 이해하기 전에 우선 Observable에 대해 알아두어야 할 점이 있다.

 

 

GitHub - ReactiveX/RxSwift: Reactive Programming in Swift

Reactive Programming in Swift. Contribute to ReactiveX/RxSwift development by creating an account on GitHub.

github.com

Observable just defines how the sequence is generated and what parameters are used for element generation. Sequence generation starts when subscribe method is called.

rx의 공식문서에 따르면 Observable은 subscribe()가 호출되는 순간에 생성된다. subscribe()가 호출되기 전까지는 스트림 생성에 관한 요소들을 정의하는 것일 뿐, 실제로 Observable은 생성된 상태가 아닌 것이다.

 

요약을 하자면 Observable의 생성은 subscribe()가 호출되는 순간이고, Observable이 어떤 스레드에서 생성될지를 결정해주는 것이 바로 subscribeOn(_ scheduler) 오퍼레이터라는 것이다. 

subscribeOn(_ scheduler) -> Observable을 생성할 스레드를 지정
ObserveOn(_ shcedular) -> Observable이 생성된 이후 스레드 변경

 

let observable = Observable<Int>.create { observer in
    observer.onNext(1)
    observer.onNext(2)
                                                
    print("[.create] in mainThread? \(Thread.isMainThread)")

    observer.onCompleted()
    return Disposables.create()
}
.observeOn(MainScheduler.instance)
.map{ next -> Int in
    print("[.map]",next, "in mainThread? \(Thread.isMainThread)")
    return next*2
}

print("[subscribe start]")    //구독시작
observable
    .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
    .subscribe(onNext: { event in
    print("[.subscribe] onNext \(event) in mainThread? \(Thread.isMainThread)")
}, onDisposed: {(
    print("[.disposed] in mainThread? \(Thread.isMainThread) \n---------------------------")
)})
.disposed(by: disposeBag)

//[subscribe start]
//[.map] 1 in mainThread? true
//[.create] in mainThread? false
//[.subscribe] onNext 2 in mainThread? true
//[.map] 2 in mainThread? true
//[.subscribe] onNext 4 in mainThread? true
//[.disposed] in mainThread? true 
//---------------------------
//[subscribe start]
//[.map] 1 in mainThread? true
//[.subscribe] onNext 2 in mainThread? true
//[.map] 2 in mainThread? true
//[.subscribe] onNext 4 in mainThread? true
//[.create] in mainThread? false
//[.disposed] in mainThread? true 
//---------------------------

두 번 수행한 출력 결과를 보면 알 수 있듯이 create 영역의 출력은 subscribe() 오퍼레이터가 호출된 이후에 등장하며, concurrent Queue에서 작업이 실행되기 때문에 mainThread 와의 출력 순서를 보장하지 않는다. 반면 ObserveOn() 오퍼레이터 이후에는 mainThread로 변경되어 동작하기에 출력 순서가 보장되는 모습을 확인할 수 있다.

 

subscribeOn()의 경우 다음 라인부터 영향을 주는 observeOn()과 달리 스트림 속 아무 라인에 적어놓아도 관계없이 적용되며, 여러 번 작성하더라도, 가장 첫 번째 subscribeOn()으로만 적용이 된다고 한다.

 

참고 영상 및 블로그

[유튜브] 곰튀김 rxswift 4시간에 끝내기 - 8    

[블로그] 이승연 님 글 [RxSwift] Scheduler 제대로 알아보기

'iOS > RxSwift' 카테고리의 다른 글

next, error, complete  (0) 2022.08.19
operator  (0) 2022.07.21
subscribe, dispose  (0) 2022.06.15
Observable stream  (0) 2022.06.08
ReactiveX  (0) 2022.05.30

subscribe( on: (Event<type>) -> Void)

Rx의 여러 오퍼레이터로 이루어진 스트림의 마지막엔 subscribe()을 통해 가공된 데이터를 확인하게 된다. 여기서 Event<타입>형태의 데이터가 들어오게 되고, Event는 next, error, completed 3가지의 값을 가질 수 있다. 

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next:
            break
        case .error:
            break
        case .completed:
            break
        }
    })
    .disposed(by: disposeBag)

next

스트림에서 넘어온 자료가 있는 경우, next에 해당한다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")    //자료 출력
            break
        case .error(let err):
            break
        case .completed:
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// next: In
// next: 4
// next: Hours

error

스트림에서 에러가 발생한 경우, error의 값을 가지게 된다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .single()    //하나의 데이터만 들어와야함.
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")
            break
        case .error(let err):
            print("err: \(err.localizedDescription)")
            break
        case .completed:
            print("completed")
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// err: The operation couldn’t be completed. (RxSwift.RxError error 5.)

 

single()오퍼레이터의 경우 하나의 데이터만 입력을 받아야 하는데, 스트림의 앞쪽에 위치한 from() 오퍼레이터의 경우 이벤트가 여러번 발생되기 때문에 에러가 발생하게된다. 이렇게 고의로 에러를 발생시켜보면 에러의 내용이 출력되는 모습을 확인할 수 있고, completed 될 수 없다고 나온다. 즉 에러가 발생하면 completed 상태에 들어가지 않고 스트림은 마무리된다.


completed

해당 스트림이 에러없이 모든 이벤트들을 내보냈을경우 completed 값을 가지게 된다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe({event in
        switch event{
        case .next(let str):
            print("next: \(str)")
            break
        case .error(let err):
            print("err: \(err.localizedDescription)")
            break
        case .completed:
            print("completed")
            break
        }
    })
    .disposed(by: disposeBag)
    
// next: RxSwift
// next: In
// next: 4
// next: Hours
// completed

해당 코드를 실행하게되면 from에 의해 생성된 4개의 자료들이 출력되고, 마지막으로 "completed"가 출력되는 모습을 볼 수 있다. 

즉 에러가 발생하지 않는다면, 발생되는 모든 이벤트를 처리하고 마지막으로 completed 상태가 되면서 마무리가 된다.


subscribe(onNext:  , onError: , onCompleted:  , onDisposed:  )

subscibe(_ on: )의 경우, switch문을 통해 event의 모든 조건에 대한 처리를 해주어야 했다면, subscribe(onNext:  ,onError:  ,onCompleted:  ,onDisposed:  )의 경우 원하는 경우의 분기만 골라서 작성이 가능하게 해주는 오퍼레이터이다.

또 다른 차이점은 onDisposed가 추가되었다는 점이다. 

Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe(onNext: { str in
        print(str)
    })
    .disposed(by: disposeBag)
    
// RxSwift
// In
// 4
// Hours

모든 경우의 값에 대해 작성해야 하는 subscribe( _ on:  )과 달리 필요한 부분만 이벤트 처리하여 더 간결한 코드 작성이 가능하다.

Observable.from(["RxSwift", "In", "4", "Hours"])
    .single()
    .subscribe(onNext: { str in
        print(str)
    }, onError: { err in
        print(err.localizedDescription)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })
    .disposed(by: disposeBag)
    
// RxSwift
// The operation couldn’t be completed. (RxSwift.RxError error 5.)
// disposed
Observable.from(["RxSwift", "In", "4", "Hours"])
    .subscribe(onNext: { str in
        print(str)
    }, onError: { err in
        print(err.localizedDescription)
    }, onCompleted: {
        print("completed")
    }, onDisposed: {
        print("disposed")
    })
    .disposed(by: disposeBag)
    
// RxSwift
// In
// 4
// Hours
// completed
// disposed

예제 코드에서 알 수 있듯, 스트림은 마지막으로 disposed 되면서 마무리된다. 강의에서 말하길, 많은 개발자들이 흔히 하는 실수로 프로세스의 종료처리를 onCompleted 부분에서 작성하게 되는데, 이렇게 되면 에러 발생시에 onCompleted 상태에 들어가지 않게 되어 프로세스의 종료처리가 일어나지 않게 된다. 따라서 프로세스의 마무리 처리를 하려면 onDisposed 부분에서 작성해야 한다.

 

[참고영상]

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 7

'iOS > RxSwift' 카테고리의 다른 글

scheduler  (0) 2022.08.31
operator  (0) 2022.07.21
subscribe, dispose  (0) 2022.06.15
Observable stream  (0) 2022.06.08
ReactiveX  (0) 2022.05.30

operator

Observable이 수행하는 메소드. Observable에서 파생되는 스트림을 구성하는 rxSwift의 핵심요소라고 할 수 있다. 

Observable에는 수행가능한 여러가지 operator가 있는데, 그중 대표적인것들에 대해서 적어보려고 한다. 

 

just()

static func just(_ element: [String]) -> Observable<[String]>
//returns an Observable sequence that contains a single element
Observable.just(_ element: )

입력 요소에 대해 그대로 반환되는 연산자이다. 예시를 통해 보자. 

@IBAction func exJust1() {
    Observable.just("Hello World")
        .subscribe(onNext: { str in
            print(str)
        })
        .disposed(by: disposeBag)
}
//Hello World!    출력

@IBAction func exJust2() {
    Observable.just(["Hello", "World"])
        .subscribe(onNext: { arr in
            print(arr)
        })
        .disposed(by: disposeBag)
}
//["Hello","World"]    출력

배열을 넣어도 배열이 통째로 출력되는 모습을 볼 수 있다.


from()

public static func from(_ array: [Element]) -> Observable<Element>
//Converts an array to an observable sequence.
Observable.from(_ array: [_])

배열의 원소들을 Observable의 형태로 리턴해준다.

@IBAction func exFrom1() {
    Observable
        .from(["RxSwift", "In", "4", "Hours"])
        .subscribe(onNext: { str in
            print(str)
        })
        .disposed(by: disposeBag)
}
//RxSwift
//In
//4
//Hours 출력

from을 통해 나오는 Observable<String>형태의 원소들이 subscribe(OnNext: )부분에서 출력이 된다.


map()

func map<Result>(_ transform: @escaping (String) throws -> Result) -> Observable<Result>
//Projects each element of an observable sequence into a new form.

swift에서의 map()과 같은 동작을 하는 operator이다. Observable에서 생성되어 내려오는 데이터들에 closure에 적힌 동작에 따라 가공하여 리턴해준다. 

@IBAction func exMap1() {
    Observable.just("Hello")
        .map { str in "\(str) RxSwift" }
        .subscribe(onNext: { str in
            print(str)
        })
        .disposed(by: disposeBag)
}
//Hello RxSwift 출력

@IBAction func exMap2() {
    Observable.from(["RxSwift", "ReactiveX"])
        .map { $0.count }
        .subscribe(onNext: { str in
            print(str)
        })
        .disposed(by: disposeBag)
}
//7
//9 출력

아마도 가장 많이 쓰게될 operator가 아닐까 생각된다.


filter()

func filter(_ predicate: @escaping (Int) throws -> Bool) -> Observable<Int>
//Filters the elements of an observable sequence based on a predicate.

swift의 filter()함수와 동일하게 동작하는 오퍼레이터이다. 서술한 조건에 맞는 경우에만 Observable을 내보낸다.

@IBAction func exFilter() {
    Observable.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
        .filter { $0 % 2 == 0 }
        .subscribe(onNext: { n in
            print(n)
        })
        .disposed(by: disposeBag)
}
//2
//4
//6
//8
//10 출력

배열요소들 중에 짝수인 요소만 출력된다.


stream

우리는 이러한 여러가지 오퍼레이터들을 통해 Observable을 가공하여 비동기 프로그래밍을 할 수 있게 된다. 앞의 글에서도 설명했지만 이러한 가공의 과정을 stream이라 부르기로 했고, 이러한 Observable을 통한 stream을 통해 비동기 프로그래밍을 할 수 있게 해주는 것이 RxSwift이다.

@IBAction func exMap3() {
    Observable.just("800x600")
        .map { $0.replacingOccurrences(of: "x", with: "/") }    //"800/600"
        .map { "https://picsum.photos/\($0)/?random" }          //"https://picsum.photos/\800/600/?random"
        .map { URL(string: $0) }                                //URL?
        .filter { $0 != nil }
        .map { $0! }                                            //URL!
        .map { try Data(contentsOf: $0) }                       //Data
        .map { UIImage(data: $0) }                              //UIImage?
        .subscribe(onNext: { image in
            self.imageView.image = image
        })
        .disposed(by: disposeBag)
}

Observable이 여러가지 오퍼레이터를 통해 어떤식으로 변화하는지 주석으로 적어놓았다. 

 

이외에도 정말 많은 오퍼레이터들이 있으며, 사용처에 따라 알맞은 오퍼레이터를 사용하면 된다. 각 오퍼레이터에 대한 상세한 내용들도 홈페이지에서 열람 가능하다.

 

[참고영상]

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 4

[유튜브] 곰튀김 RxSwift 4시간에 끝내기 - 5

'iOS > RxSwift' 카테고리의 다른 글

scheduler  (0) 2022.08.31
next, error, complete  (0) 2022.08.19
subscribe, dispose  (0) 2022.06.15
Observable stream  (0) 2022.06.08
ReactiveX  (0) 2022.05.30

자신의 앱에 구글 애드몹을 사용한다면, 추적광고 때문에 반드시 사용자에게 해당 알림을 띄워야만 앱심사에서 통과된다. 일반적인 경우 앱의 최초실행에만 알림이 뜨면 되는 터라, 앱 출시 당시 정상 동작하는것만 확인하고서는 그뒤로는 잘 되겠거니 하고서 따로 확인을 하지 않았다.

이후 아이폰의 iOS업데이트의 영향인지 혹은 내가 변경한 코드들의 영향때문인지는 모르겠으나 일반적인 호출로는 알림이 뜨지않아 IDFA를 가져오는 과정에서 "Not Determined" 상태가 리턴되는 문제가 발생하였다. 

func requestPermission() {
    ATTrackingManager.requestTrackingAuthorization { status in
        switch status {
        case .authorized:
            // Tracking authorization dialog was shown
            // and we are authorized
            print("[googleAdmob]: Authorized")
            // Now that we are authorized we can get the IDFA
            print(ASIdentifierManager.shared().advertisingIdentifier)
        case .denied:
            // Tracking authorization dialog was
            // shown and permission is denied
            print("[googleAdmob]: Denied")
        case .notDetermined:
            // Tracking authorization dialog has not been shown
            print("[googleAdmob]: Not Determined")
        case .restricted: print("[googleAdmob]: Restricted")
        @unknown default: print("[googleAdmob]: Unknown")
        }
    }
}

앱추적 권한 상태는 크게 4가지. Authorized(허용됨), denied(거부됨), NotDetermined(결정안됨), restricted(제한됨)

주석들을 보면 알겠지만 추적권한 알림이 뜨지 않는 상태라는 걸 알수있다. 해결법은 StackOverFlow에서 손쉽게 찾을수 있었다. 

DispatchQueue.main.asyncAfter(deadline: .now()+0.2){
    self.requestPermission()
}

requestPermission() 함수를 호출하던 부분을 비동기 방식, 예약호출을 통해 실행하니 해결되었다. 현재시간에서 0.01초 단위로 늘려보았고, 0.13초부터 알림이 뜨기시작했다. 혹시 모르니 0.2초 예약호출로 작성하였고, "Not Determined"문제는 이렇게 해결되었다.

+ Recent posts