6

传统上,我一直使用 NSOperation 子类创建和管理自己的 NSURLConnection 的模式。NSOperation 子类由视图控制器实例化,并将在完成工作之前不打扰控制器。当它完成检索数据时,它会执行视图控制器提供的完成块。

  1. ViewController 实例化 NSOperation 子类(封装了 URL、参数等)
  2. NSOperation 子类实例化 NSURLConnection(执行同步请求并检索数据)
  3. NSURLConnection 将数据转储到 NSOperation-subclass
  4. NSOperation-subclass 执行视图控制器提供的完成块。

我现在正在尝试使用 NSURLSession 实现相同的模式。我希望能够将发出网络请求所需的 url 和参数封装在单个对象中。我是否使用 NSURLSession 子类或 NSURLSessionTask 子类来实现这一点?

我喜欢根据参与者设计模式为每个网络操作创建单独的类。

4

4 回答 4

11

NSURLSessionTask(及其子类)看起来有点像操作,但它们不是。因此,您可以在过渡到 时从代码中删除操作NSURLSession,但如果这样做,您将失去某些NSOperation功能(依赖关系、控制并发度等)。我不确定为什么在过渡到NSURLSession. 就个人而言,我曾经NSURLConnection在操作中包装 a 的任何地方,现在都使用操作包装 a NSURLSessionTask

顺便说一句,虽然,其中一个重要的烦恼NSURLSession任务委托被设置在会话对象中。我们可以猜测苹果为什么这样做,但它有各种不幸的含义。显然,您可以通过使用基于块的工厂方法来创建任务来解决此问题,但是如果您碰巧需要,您将失去委托 API 的丰富性。

这意味着如果使用基于块的任务工厂方法,将任务包装在并发NSOperation子类中是相当明显的。但是,如果使用基于委托的任务,但是,如果您想要任务的自定义处理程序,您必须通过维护任务标识符和适当的完成块(我个人将其放入会话管理器对象)之间的映射来进行一些愚蠢的操作我用来包装的NSURLSession)。(仅供参考,我相信在即将到来的 AFNetworking 更新中也有望实现这样的实现。请参阅AFNetworking github 站点上关于问题 1504的讨论的后半部分。)

无论如何,其他人已经回答了如何NSURLConnection用非基于操作的代码替换基于操作的NSURLSession代码的问题,但我个人建议继续使用操作。


顺便说一句,我NSURLSession在 github 上上传了一个基于操作的实现的示例实现:https ://github.com/robertmryan/NetworkManager

这并不是一个完整的解决方案,而是说明了如何NSURLSession使用NSOperation子类实现基于委托的想法。

于 2014-02-05T16:55:03.023 回答
3

You can use the same pattern, replacing NSURLConnection with NSURLSessionTask subclasses (e.g. NSURLSessionDataTask).

As @CouchDeveloper suggests in the comments, an alternative is to wrap NSURLSessionTask in an non-NSOperation object with asynchronous semantics (cancel, resume, etc.). This wrapper object would do little more than encode and decode parameters, delegating most operations to the wrapped task.

In either case, to instantiate NSURLSessionTask, you will need a NSURLSession. (NSURLSession is the NSURLSessionTask factory.) If all of your operations use the same configuration (cookies, proxy, caching, etc.), you can simply use the shared session (+[NSURLSession sharedSession]). If they need different configurations, you'll have to give them a NSURLSession or enough information to create their own.

于 2014-01-15T21:46:01.207 回答
0

我希望能够将发出网络请求所需的 url 和参数封装在单个对象中。我是否使用 NSURLSession 子类或 NSURLSessionTask 子类来实现这一点?

你所描述的是NSURLRequest. 两者都NSURLSession需要NSURLConnectionsNSURLRequest来执行网络连接(这NSURLRequest就是它正在执行的操作)。您似乎想要的是一组用于生成不同的、专门的NSURLRequests. 例如,描述“获取我的邮件”请求的请求。您可以通过在其自身上创建一个类别来轻松地做到这一点NSURLRequest。例子:

@implementation NSURLRequest (Mail)

+ (instancetype) mailRequestForUser:(NSString *)user {
    NSURLRequest *result = nil;
    result = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"someServer""]];
    // set user in a header, whatever

    // return immutable copy
    return [result copy];
}

@end

创建与上述类似的内容以满足您的需求。NSURLRequest然后可以使用生成的 s 来创建连接。在 Cocoa 中,这是一种非常典型的自定义对象模式,而不是子类化。子类化非常罕见——相反,Cocoa 有其他机制来定制类行为,从工厂方法(像上面的“便利方法”)到委托(一个对象被赋予另一个对象的行为责任)。

于 2014-06-11T01:27:38.960 回答
0

我所做的 - 只是允许OperationURLSessionTask保持分开。为此,我进行了通用异步块操作,用于实例化、恢复、取消URLSessionTask

因此,在 ViewController 或 Data 层中,我仍在使用 chained Operationswith URLSessionTasksinside。下面的示例代码可以通过子类化来扩展AsynchronousBlockOperation

文件 1. 通用异步操作。

open class AsynchronousOperation: Operation {

   private var lockOfProperties = NonRecursiveLock.makeDefaultLock()

   private var mFinished = false
   private var mExecuting = false

   public override init() {
      super.init()
   }

   /// Subclasses must launch job here.
   ///
   /// **Note** called between willChangeValueForKey and didChangeValueForKey calls, but after property mExecuting is set.
   open func onStart() {
   }

   /// Subclasses must cancel job here.
   ///
   /// **Note** called immediately after calling super.cancel().
   open func onCancel() {
   }

   /// Subclasses must release job here.
   ///
   /// **Note** called between willChangeValueForKey and didChangeValueForKey calls,
   /// but after properties mExecuting and mFinished are set.
   open func onFinish() {
   }
}

extension AsynchronousOperation {

   public final override var isAsynchronous: Bool {
      return true
   }

   public final override var isExecuting: Bool {
      return lockOfProperties.synchronized { mExecuting }
   }

   public final override var isFinished: Bool {
      return lockOfProperties.synchronized { mFinished }
   }
}

extension AsynchronousOperation {

   public final override func start() {
      if isCancelled || isFinished || isExecuting {
         return
      }
      willChangeValue(forKey: #keyPath(Operation.isExecuting))
      lockOfProperties.synchronized { mExecuting = true }
      onStart()
      didChangeValue(forKey: #keyPath(Operation.isExecuting))
   }

   public final override func cancel() {
      super.cancel()
      if isExecuting {
         onCancel()
         finish()
      } else {
         onCancel()
         lockOfProperties.synchronized {
            mExecuting = false
            mFinished = true
         }
      }
   }

   public final func finish() {
      willChangeValue(forKey: #keyPath(Operation.isExecuting))
      willChangeValue(forKey: #keyPath(Operation.isFinished))
      lockOfProperties.synchronized {
         mExecuting = false
         mFinished = true
      }
      onFinish()
      didChangeValue(forKey: #keyPath(Operation.isExecuting))
      didChangeValue(forKey: #keyPath(Operation.isFinished))
   }
}

文件 2. 基于异步块的操作:

open class AsynchronousBlockOperation: AsynchronousOperation {

   public typealias WorkItemType = OperationCancelationType & OperationResumingType
   public typealias FinaliseBlock = () -> Void
   public typealias WorkItemBlock = (@escaping FinaliseBlock) -> WorkItemType?

   private var executionBlock: WorkItemBlock
   private var blockExecutionQueue: DispatchQueue?
   private var workItemToken: WorkItemType?

   public init(blockExecutionQueue: DispatchQueue? = nil, executionBlock: @escaping WorkItemBlock) {
      self.blockExecutionQueue = blockExecutionQueue
      self.executionBlock = executionBlock
      super.init()
   }

   open override func onStart() {
      if let queue = blockExecutionQueue {
         queue.async {
            self.execute()
         }
      } else {
         execute()
      }
   }

   open override func onCancel() {
      workItemToken?.cancelOperation()
   }

   private func execute() {
      workItemToken = executionBlock { [weak self] in
         self?.finish()
      }
      if var token = workItemToken {
         token.resumeOperation()
      } else {
         finish()
      }
   }
}

文件 3. 协议

public protocol OperationCancelationType {
   mutating func cancelOperation()
}

public protocol OperationResumingType {
   mutating func resumeOperation()
}

extension URLSessionTask: OperationCancelationType {
   public func cancelOperation() {
      cancel()
   }
}

extension URLSessionTask: OperationResumingType {
   public func resumeOperation() {
      resume()
   }
}

用法:

let operation = AsynchronousBlockOperation { [weak self] finalise in
   return session.dataTask(with: url) {
      ...
      finalise() // This will finish operation
   }
}
于 2018-04-02T17:43:31.247 回答