2017-02-06 11 views
10

AlamoFireの提供するOAuthの実装例を得ることができました。しかし、私は特定のコード行とそれがどのように動作するのかを理解しようとしています。AlamoFireの理解OAuthの例

全例:

class OAuth2Handler: RequestAdapter, RequestRetrier { 
    private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?, _ refreshToken: String?) -> Void 

    private let sessionManager: SessionManager = { 
     let configuration = URLSessionConfiguration.default 
     configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders 

     return SessionManager(configuration: configuration) 
    }() 

    private let lock = NSLock() 

    private var clientID: String 
    private var baseURLString: String 
    private var accessToken: String 
    private var refreshToken: String 

    private var isRefreshing = false 
    private var requestsToRetry: [RequestRetryCompletion] = [] 

    // MARK: - Initialization 

    public init(clientID: String, baseURLString: String, accessToken: String, refreshToken: String) { 
     self.clientID = clientID 
     self.baseURLString = baseURLString 
     self.accessToken = accessToken 
     self.refreshToken = refreshToken 
    } 

    // MARK: - RequestAdapter 

    func adapt(_ urlRequest: URLRequest) throws -> URLRequest { 
     if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) { 
      var urlRequest = urlRequest 
      urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") 
      return urlRequest 
     } 

     return urlRequest 
    } 

    // MARK: - RequestRetrier 

    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) { 
     lock.lock() ; defer { lock.unlock() } 

     if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 { 
      requestsToRetry.append(completion) 

      if !isRefreshing { 
       refreshTokens { [weak self] succeeded, accessToken, refreshToken in 
        guard let strongSelf = self else { return } 

        strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() } 

        if let accessToken = accessToken, let refreshToken = refreshToken { 
         strongSelf.accessToken = accessToken 
         strongSelf.refreshToken = refreshToken 
        } 

        strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } 
        strongSelf.requestsToRetry.removeAll() 
       } 
      } 
     } else { 
      completion(false, 0.0) 
     } 
    } 

    // MARK: - Private - Refresh Tokens 

    private func refreshTokens(completion: @escaping RefreshCompletion) { 
     guard !isRefreshing else { return } 

     isRefreshing = true 

     let urlString = "\(baseURLString)/oauth2/token" 

     let parameters: [String: Any] = [ 
      "access_token": accessToken, 
      "refresh_token": refreshToken, 
      "client_id": clientID, 
      "grant_type": "refresh_token" 
     ] 

     sessionManager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default) 
      .responseJSON { [weak self] response in 
       guard let strongSelf = self else { return } 

       if 
        let json = response.result.value as? [String: Any], 
        let accessToken = json["access_token"] as? String, 
        let refreshToken = json["refresh_token"] as? String 
       { 
        completion(true, accessToken, refreshToken) 
       } else { 
        completion(false, nil, nil) 
       } 

       strongSelf.isRefreshing = false 
      } 
    } 
} 

質問:

[weak self] succeeded, accessToken, refreshToken in 
        guard let strongSelf = self else { return } 
  1. [weak self]strongSelfためguardの目的は何ですか?

    requestsToRetry.append(completion) 
    
        if !isRefreshing { 
         refreshTokens { [weak self] succeeded, accessToken, refreshToken in 
          guard let strongSelf = self else { return } 
    
          //Implementation 
    
          strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } 
          strongSelf.requestsToRetry.removeAll() 
         } 
        } 
    
  2. この要求はどのように再試行されますか? requestsToRetryはただの配列ですRequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval)どのような要求を再試行するかはどのように分かりますか?

strongSelf.lock.lock()

  1. NSLockはちょうどこの方法の実行中self(OAuth2Handler)が他のスレッドによってアクセスされることを可能にしませんか?
+2

1は 'self'への強い参照を得て、次のコードでは' self'を使用しようとしません。なぜなら、そのコンテキストで 'self'が弱いため、強い参照が最後に解放されるからです保持サイクルは発生しません。 alamofireに精通していないので、残りの部分は確かに言う。 – Fonix

答えて

7

1)は正確に同じFonixでコメントし、あなたはselfだった場合は、サイクルを維持収集するために開始nilをすることを避けるためにselfへの強い参照を持っている...

私はにrefeerだ:

[weak self] ... in 
guard let strongSelf = self else { return } 

selfは、非同期にディスパッチされたブロックでキャプチャされるため、暗黙的に保持され、ブロックの終了時に再び解放されます。つまり、ブロックが終了するまでselfが拡張されます。 selfがnil

2に等しい場合、このコードを作ることは、あなたが言及したラインによる)selfの寿命を延長しないブロックを実行することを決定するために避ける:そこ

if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 { 
      requestsToRetry.append(completion) 
      .. 

再起動が必要なすべてのリクエストが含まれているrequestsToRetryという配列です。

strongSelf.requestsToRetry.forEach { $0(succeeded, 0.0) } 
strongSelf.requestsToRetry.removeAll() 

、すべての項目を起動します。このコードでは、配列にrequestToRetry、配列をコードforEachあなたのループで (サーバはステータスコード401を返すとき)401ステータスコードを持つすべての要求を追加します。サイクルが終了したら、すべてのアイテムを削除します。

は実際には、ソースレポート:

public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void 

public protocol RequestRetrier { 
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) 
} 

あなたが見つけることができる詳細here

3)を正確にあなたが直面している頻繁に同時実行の問題は、アクセスに関連した1 /共有リソースを変更している言ったように異なるスレッドから。 lock.lock()は、アイテムが変更されているときに他の実行ブロックをロックするソリューションです。 deferのコードは、ブロックのロックを解除する機能を残す直前に呼び出されます。

関連する問題