Пусть делегаты запускаются не в главном потоке - PullRequest
0 голосов
/ 07 января 2019

Мне вообще интересно, как разрешить быстрым делегатам работать в выделенном потоке, отличном от основного потока.

Более конкретно, в настоящее время я использую 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 голосов
/ 07 января 2019

Да

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

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

    apiQueue.async {
        ...
    }
}

Нет причин каждый раз получать очередь; Вы можете просто сохранить его как собственность.

Я знаю, что это не то, о чем ты думаешь, но это лучший подход. Просто отправьте, когда вам это нужно.

Можно создать нечто вроде того, о чем вы на самом деле думаете в ObjC, используя forwardInvocation:, но это не очень хорошо переводит в Swift (базовый механизм не может быть реализован в Swift), и я не Я не рекомендую это. Батут - это объект (иногда объект NSProxy), который может принять любое сообщение, что-то с ним сделать (например, переместить его в другую очередь), а затем повторно передать его другому объекту. Проблема в том, что трудно сказать компилятору: «Поверьте мне, он реализует каждый метод, который вам нужен во время выполнения» (потому что вы не можете этого пообещать, поэтому он может потерпеть крах). Даже в ObjC они часто оказывались более сложными, чем стоили, и единственная причина, по которой они стоили в первую очередь, это то, что ObjC не всегда имел GCD или блоки, и когда были добавлены блоки, синтаксис был Головная боль. В Swift это намного проще. Просто добавьте .async вызов.

Просто чтобы показать, как выглядит батут, вот как выглядит очередь, использующая очередь операций (очень похожую на очередь отправки). Это написано в pre-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 сегодня. Это должно спасти много неприятностей, и это не так.

(Примечание: очереди - это не то же самое, что потоки. Проблема в том, к каким объектам очереди отправляются, а не в каком потоке.)

0 голосов
/ 07 января 2019

Я не думаю, что это возможно, если эта библиотека специально не поддерживает это.

Одна вещь, которую вы могли бы сделать, это создать «делегированный» класс делегата, который перенаправляет все вызовы метода делегата на конкретную DispatchQueue.

Это не идеальное решение, но, по крайней мере, вы можете держать свой HueApiManager немного чище, потому что вам не нужно apiThread.async везде.

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...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...