티스토리 뷰

 

Crash Log

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [3557]
Triggered by Thread:  0
Thread 0 name:
Thread 0 Crashed:
0   Foundation                      0x000000018b10777c __NSOQSchedule + 252 (NSOperation.m:285)
1   Foundation                      0x000000018b1076ec __NSOQSchedule + 108 (NSOperation.m:1766)
2   Foundation                      0x000000018b109344 __addOperations + 936 (NSOperation.m:2604)

 

 

재현 조건 

1. OperationQueue 와 AsyncOperation 사용

2. OperationQueue 가 suspend 된 상태에서 cancelAllOperation 호출 

// Operation Queue

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
internal class AsyncOperation: Operation {
    
    //MARK: - Property Override
    override var isAsynchronous: Bool {
        return true
    }
    
    override var isExecuting: Bool {
        return self.state == .executing
    }
    
    override var isFinished: Bool {
        return self.state == .finished
    }
    
    private let completion: AsyncOperationCompletion?
    init(completion: AsyncOperationCompletion?) {
        self.completion = completion
        super.init()
    }
    
    //MARK: - Operation State
    enum State {
        case ready, executing, finished
        func keyString() -> String {
            switch self {
            case .ready:
                return "isReady"
            case .executing:
                return "isExecuting"
            case .finished:
                return "isFinished"
            }
        }
    }
    
    private var state = State.ready {
        willSet {
            willChangeValue(forKey: self.state.keyString())
            willChangeValue(forKey: newValue.keyString())
        }
        didSet {
            didChangeValue(forKey: self.state.keyString())
            didChangeValue(forKey: oldValue.keyString())
        }
    }
    
    //MARK: - Operation Job
    override func start() {
        if self.isCancelled {
            self.finishByCancel()
            return
        }
        self.state = .executing
        self.async()
    }
    
    internal func async() {
        //Must Override
        preconditionFailure()
    }
    
    final internal func finishByCancel() {
        self.finish(result: nil,
                    error: AsyncOperationError.cancelled)
    }
    
    final internal func finish(result: Any?,
                               error: Error?) {
        if self.state == .finished {
            return
        }
        
        self.state = .finished
        
        guard let completion = self.completion else {
            return
        }
        
        DispatchQueue.main.async {
            completion(result, error)
        }
    }
    
}

 

 

 

현상

쌓여있는 Operation 들이 cancel 을 받으며
추후 이상 동작하는 현상이 발견
(addOperation 시, isSuspend 시, state 변경 시 crash 발생)

 

 

 

해결 

OperationQueue 가 suspend 된 시점에 cancel 을 직접하지 않으면 정상동작한다. 

따로 cancel method 를 상속받지 않도록 하고

직접 호출하지 않도록 한다. 

 

cancelAllOperation 호출 시
Operation 내부적으로 isCancelled 상태를 가지게 되고,
시작 시 isCancelled 면 동작을 하지 않도록 예외 처리한다. 

 

 

 

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함