让代理在主线程之外运行

时间:2019-01-07 12:13:50

标签: swift

我通常想知道如何让迅速的代表在除主线程之外的专用线程中运行。

更具体地说,我当前正在使用HueSDK4EDK建立我的应用程序与Hue桥的连接。该过程的一部分是定义状态观察者和连接观察者(作为委托)以处理传入事件。

private func buildBridge(withBridge bridge : BridgeInfoModel) -> PHSBridge {
    return PHSBridge.init(block: { (builder) in
        ...

        builder?.bridgeConnectionObserver = self
        builder?.add(self)
    }, withAppName: AppBundleName, withDeviceName: DeviceName)
}

委托是在扩展中实现的,例如连接观察器:

extension HueApiManager : PHSBridgeConnectionObserver  {
    func bridgeConnection(_ bridgeConnection: PHSBridgeConnection!, handle connectionEvent: PHSBridgeConnectionEvent) {
    ...
    }
}

由于连接观察器中的某些代码可能会占用大量时间,因此我想知道是否有比这更优雅的方法:

func bridgeConnection(_ bridgeConnection: PHSBridgeConnection!, handle connectionEvent: PHSBridgeConnectionEvent) {
    let apiThread = DispatchQueue.global(qos: .utility)

    apiThread.async {
        ...
    }
}

非常感谢!

2 个答案:

答案 0 :(得分:2)

是:

let apiThread = DispatchQueue.global(qos: .utility)

func bridgeConnection(_ bridgeConnection: PHSBridgeConnection!, handle connectionEvent: PHSBridgeConnectionEvent) {

    apiQueue.async {
        ...
    }
}

没有理由每次都获取队列;您可以将其存储为属性。

我知道这不是您要考虑的,但这是最好的方法。只需在需要时执行调度即可。

可以使用forwardInvocation:构建与您在ObjC中实际想到的东西类似的东西,但是它不能很好地转换为Swift(底层机器无法在Swift中实现),我不推荐它。蹦床是一个对象(有时是NSProxy对象),可以接受任何消息,对其执行某些操作(例如将其移至另一个队列),然后将其重新分发给另一个对象。问题是很难告诉编译器“相信我,这将在运行时实现您需要的所有方法”(因为您实际上无法承诺,因此可能会崩溃)。即使在ObjC中,这些问题通常也比他们应承担的麻烦多得多,而首先值得承担这些麻烦的唯一原因是ObjC并不总是具有GCD或块,并且在添加块时,语法是头痛。在Swift中,它要简单得多。只需添加.async调用即可。

不过,只是为了说明蹦床的样子,这是使用操作队列(非常类似于调度队列)的样子。这是用ARC之前的ObjC编写的,不应视为现代ObjC的示例。

#import <Foundation/Foundation.h>

@interface OperationQueueInvocationTrampoline : NSObject
{
    @private
    NSOperationQueue *myQueue;
    id myTarget;
}

- (id)initWithQueue:(NSOperationQueue *)queue;
@end

#import "OperationQueueInvocationTrampoline.h"

@interface OperationQueueInvocationTrampoline ()
@property (nonatomic, readwrite, retain) NSOperationQueue *queue;
@property (nonatomic, readwrite, assign) id target;
@end

@implementation OperationQueueInvocationTrampoline

@synthesize queue = myQueue;
@synthesize target = myTarget;

- (id)initWithQueue:(NSOperationQueue *)queue
{
    self = [super init];
    if (self != nil)
    {
        self.queue = queue;
    }

    return self;
}

- (void)dealloc
{
    [myQueue release];
    myQueue = nil;

    myTarget = nil;

    [super dealloc];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    return [self.target methodSignatureForSelector:selector];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    [invocation setTarget:self.target];
    self.target = nil;

    NSOperation *operation = [[[NSInvocationOperation alloc] initWithInvocation:invocation] autorelease];

    [self.queue addOperation:operation];
}

- (id)prepareWithInvocationTarget:(id)target
{
    self.target = target;

    return self;
}

@end

您将像这样使用它:

id delegate = [[OperationQueueInvocationTrampoline alloc] initWithTarget:self];

[otherThing setDelegate:delegate];

当大多数协议都是非正式的时,这种方法工作得很好(因此这里没有类型检查,您可以通过id),但是在没有警告的情况下进行编译变得越来越麻烦了,而且今天在Swift中肯定是头疼的事情。它必须省去很多麻烦,但事实并非如此。

(附带说明:队列与线程不是一回事。眼前的问题是队列将事物分派到什么队列,而不是它们在什么线程上运行。)

答案 1 :(得分:0)

I don't think this is possible unless that library specifically supports it.

One thing you could do is create a "proxy" delegate class that forwards all delegate method calls on a specific DispatchQueue.

It's not a perfect solution, but at least you can keep your HueApiManager a bit cleaner because you do not need apiThread.async everywhere.

class DelegateProxy: PHSBridgeConnectionObserver {
  weak var delegate: PHSBridgeConnectionObserver?
  var queue = DispatchQueue.global(qos: .utility)

  func bridgeConnection(_ bridgeConnection: PHSBridgeConnection!, handle connectionEvent: PHSBridgeConnectionEvent) {
    queue.async {
      delegate?.bridgeConnection(bridgeConnection, handle: connectionEvent)
    }
  }

  // Other delegate methods...
}