我有两个NSOperation
负责下载和解析。下载操作成功后,我收到一些NSData
,然后我想将该数据设置为解析操作要使用的数据:
init(context: NSManagedObjectContext, completionHandler: Void -> Void) {
downloadOperation = DownloadActivitiesOperation() { data in
self.parseOperation.data = data
}
parseOperation = ParseActivitiesOperation(context: context)
let finishOperation = NSBlockOperation(block: completionHandler)
parseOperation.addDependency(downloadOperation)
finishOperation.addDependency(parseOperation)
super.init(operations: [downloadOperation, parseOperation, finishOperation])
name = "Get Activities"
}
然而,这并不起作用,因为我在调用self
之前尝试在我的下载完成块中使用super.init
。
我的问题是,当尝试将一个操作的结果传递给链中的下一个操作时,最好的方法是什么?
答案 0 :(得分:18)
NSOperation
的每个实例都包含依赖项数组。完成后,不会从此阵列中删除操作。您可以使用这些对象来访问数据:
class DownloadActivitiesOperation: NSOperation {
var data: NSData?
...
// set self.data when it has downloaded
}
class ParseActivitiesOperation: NSOperation {
func main() {
if let downloadOp = self.dependencies.last as? DownloadActivitiesOperation {
let dataToParse = downloadOp.data
...
}
}
}
等等。
答案 1 :(得分:7)
看起来您正在使用Advanced NSOperations上的WWDC2015谈话中GroupOperation
的某种实现方式。在谈话的示例代码中,他们使用缓存文件在下载程序和解析器之间传递数据。
来自GetEarthquakesOperation
类的摘录:
let cachesFolder = try! NSFileManager.defaultManager().URLForDirectory(.CachesDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
let cacheFile = cachesFolder.URLByAppendingPathComponent("earthquakes.json")
/*
This operation is made of three child operations:
1. The operation to download the JSON feed
2. The operation to parse the JSON feed and insert the elements into the Core Data store
3. The operation to invoke the completion handler
*/
downloadOperation = DownloadEarthquakesOperation(cacheFile: cacheFile)
parseOperation = ParseEarthquakesOperation(cacheFile: cacheFile, context: context)
我在我的一个项目中的当前解决方案是将结果包装在一个类中并将其传递给两个操作:
class OperationResultWrapper<T> {
var result: T?
}
let wrapper = OperationResultWrapper<NSData>()
downloadOperation = DownloadEarthquakesOperation(resultWrapper: wrapper)
parseOperation = ParseEarthquakesOperation(dataWrapper: wrapper, context: context)
答案 2 :(得分:2)
我刚刚完成移动大部分生产代码以使用NSOperation和NSOperationQueues。分享结果(通知,代表)的典型解决方案看起来既麻烦又笨重,所以这是我的解决方案。
子类NSOperationQueue包含线程安全的可变字典实例属性。我将此处发布的线程安全可变字典代码改编为我的子类JobOperationQueue:https://www.guiguan.net/ggmutabledictionary-thread-safe-nsmutabledictionary/
子类NSOperation包含一个返回其拥有/初始JobOperationQueue的引用。这确保了操作总能找到它的所有者,即使代码必须在不同的队列上运行(这比我想象的更多!)。将子类方法添加到JobOperationQueue,以便在通过addOperation:或addOperations将操作添加到队列时设置此值:
作为操作流程,他们可以将值添加到队列的字典中,并访问早期进程放置的值。
我对这种方法非常满意,它解决了很多问题。
小心竞争条件 - 如果一个操作必须有来自另一个操作的值,请确保明确添加依赖关系以确保操作顺序。
这是我的两个主要课程。我还添加了双向依赖信息,我发现在操作必须生成子操作但希望维护依赖关系网络的情况下,这些信息非常有用。在这种情况下,您必须知道谁依赖于原始操作,以便您可以将依赖项传播到生成的操作。
//
// JobOperation.h
//
//
// Created by Terry Grossman on 9/17/15.
//
#import <Foundation/Foundation.h>
#import "JobOperationQueue.h"
#import "ThreadSafeMutableDictionary.h"
#import "ThreadSafeMutableArray.h"
@interface JobOperation : NSOperation
@property (strong, atomic) ThreadSafeMutableArray *dependents;
@property (strong, atomic) NSDate *enqueueDate;
@property (weak, atomic) JobOperationQueue *homeJobQueue;
-(ThreadSafeMutableDictionary *)getJobDict;
@end
//
// JobOperation.m
//
//
// Created by Terry Grossman on 9/17/15.
//
#import "JobOperation.h"
@implementation JobOperation
- (id)init
{
if((self = [super init])) {
_dependents = [[ThreadSafeMutableArray alloc] init];
}
return self;
}
-(ThreadSafeMutableDictionary *)getJobDict
{
id owningQueue = self.homeJobQueue;
if (owningQueue && [owningQueue isKindOfClass:[JobOperationQueue class]])
{
return ((JobOperationQueue *)owningQueue).jobDictionary;
}
// try to be robust -- handle weird situations
owningQueue = [NSOperationQueue currentQueue];
if (owningQueue && [owningQueue isKindOfClass:[JobOperationQueue class]])
{
return ((JobOperationQueue *)owningQueue).jobDictionary;
}
return nil;
}
-(void) addDependency:(NSOperation *)op
{
[super addDependency:op]; // this adds op into our list of dependencies
if ([op isKindOfClass:[JobOperation class]])
{
[((JobOperation *)op).dependents addObject:self]; // let the other job op know we are depending on them
}
}
@end
//
// JobOperationQueue.h
//
//
// Created by Terry Grossman on 9/17/15.
//
#import <Foundation/Foundation.h>
#import "ThreadSafeMutableDictionary.h"
// A subclass of NSOperationQueue
// Adds a thread-safe dictionary that queue operations can read/write
// in order to share operation results with other operations.
@interface JobOperationQueue : NSOperationQueue
// If data needs to be passed to or between job operations
@property (strong, atomic) ThreadSafeMutableDictionary *jobDictionary;
-(void)addOperation:(NSOperation *)operation;
-(void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
+(BOOL) checkQueue:(JobOperationQueue *)queue hasOpsOlderThan:(NSInteger)secondsThreshold cancelStaleOps:(BOOL)cancelOps;
@end
//
// JobOperationQueue.m
//
//
// Created by Terry Grossman on 9/17/15.
//
#import "JobOperationQueue.h"
#import "JobOperation.h"
@implementation JobOperationQueue
// if this method returns NO, should set the queue to nil and alloc a new one
+(BOOL) checkQueue:(JobOperationQueue *)queue hasOpsOlderThan:(NSInteger)secondsThreshold cancelStaleOps:(BOOL)cancelOps
{
if (queue == nil)
{
return NO;
}
if ([queue operationCount] > 0)
{
NSLog(@"previous share still processing!");
// recently started or stale? Check the enqueue date of the first op.
JobOperation *oldOp = [[queue operations] objectAtIndex:0];
NSTimeInterval sourceSeconds = [[NSDate date] timeIntervalSinceReferenceDate];
NSTimeInterval destinationSeconds = [oldOp.enqueueDate timeIntervalSinceReferenceDate];
double diff = fabs( destinationSeconds - sourceSeconds );
if (diff > secondsThreshold)
{
// more than three minutes old! Let's cancel them and tell caller to proceed
[queue cancelAllOperations];
return NO;
}
else
{
return YES;
}
}
return NO;
}
-(id) init;
{
if((self = [super init])) {
_jobDictionary = [[ThreadSafeMutableDictionary alloc] initWithCapacity:12];
}
return self;
}
-(void)addOperation:(NSOperation *)operation;
{
if (operation == nil)
{
return;
}
if ([operation isKindOfClass:[JobOperation class]])
{
((JobOperation *)operation).enqueueDate = [NSDate date];
//((JobOperation *)operation).homeQueueT = self.underlyingQueue; // dispatch_get_current_queue();
((JobOperation *)operation).homeJobQueue = self;
}
[super addOperation:operation];
}
-(void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;
{
for (NSOperation *operation in ops)
{
if ([operation isKindOfClass:[JobOperation class]])
{
((JobOperation *)operation).enqueueDate = [NSDate date];
//((JobOperation *)operation).homeQueueT = self.underlyingQueue; //dispatch_get_current_queue();
((JobOperation *)operation).homeJobQueue = self;
}
}
[super addOperations:ops waitUntilFinished:wait];
}
@end
答案 3 :(得分:1)
您可以先使用type=video
创建相关NSOperation
链,然后初始化您的属性并最后调用self
。
super.init
注意:我觉得您将init(context: NSManagedObjectContext, completionHandler: () -> Void) {
let finishOperation = NSBlockOperation(block: completionHandler)
let parseOperation = ParseActivitiesOperation(context: context)
finishOperation.addDependency(parseOperation)
let downloadOperation = DownloadActivitiesOperation() { data in
parseOperation.data = data
}
parseOperation.addDependency(downloadOperation)
self.parseOperation = parseOperation
self.downloadOperation = downloadOperation
self.name = "Get Activities"
super.init(operations: [downloadOperation, parseOperation, finishOperation])
}
和downloadOperation
存储在属性中并且将数组传递给超级类似乎很奇怪,但我并不感兴趣。知道你的其余代码。
答案 4 :(得分:0)
你可以在开始阻止之前创建一个弱变量。
尝试在阻止开始之前添加此行:
<table class="mytables">
<thead>
<tr>
<th> header col 1 </th>
<th> header col 2 </th>
<th> header col 3 </th>
</tr>
<thead>
<tbody>
<tr>
<td class="col_span"> A </td>
<td class="col_span"></td>
<td> B </th>
</tr>
<tr>
<td> C </th>
<td> D </th>
<td> E </th>
</tr>
<tr>
<td> F </th>
<td> G </th>
<td> H </th>
</tr>
</tbody>
</table>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
答案 5 :(得分:0)
感谢ilya的回答,我注意到我通过他们的依赖项数组通过我的Operation子类访问了其他操作。
最后我想出了这个扩展名:
extension Operation{
func getOperationFromDependancies<T:Operation>(withType operationType:T.Type) -> T?{
for dependency in self.dependencies {
if let operation = dependency as? T{
return operation
}
}
return nil
}
}
然后假设你使用两个操作,一个是操作下载,另一个是操作两个处理下载的文件,你的main()将包含如下内容:
override func main(){
let downloadOperation =
self.getOperationFromDependencies(withType:DownloadOperation.self)
let downloadedFile = downloadedOperation?.downloadedFile
//Process file here
}