1

クラスに次のセレクターがあります。

    - (SoapRequest*) loginAndConnect: (id) target action: (SEL) action credentials: (WSAcredentials*) arg0 dialoutInfo: (WSAdialoutInfo*) arg1;

アクションとして別のセレクターではなくブロックで呼び出すことができるようにしたいのですが、これを行う方法に関する情報が見つかりません。何かのようなもの:

[service loginAndConnect:self  credentials:credentials dialoutInfo:dialoutInfo action:^(SoapRequest* aResult) {
    // more code here
}];

これを達成するための最良の方法は何ですか?

更新* *

これはほとんど機能していますが、completionBlock オブジェクトの objc_release で例外が発生しています。ターゲットを保持することと関係があると確信していますが、それを修正する方法がわかりません。現在のコードは次のとおりです。

- (BOOL) checkService {

WSAcredentials* credentials = [[WSAcredentials alloc] init];
WSAdialoutInfo* dialoutInfo = [[WSAdialoutInfo alloc] init];
if (!service) {
    service = [[WSAWebSocketAdapterService alloc] init];
}
__block SoapRequest* request;
[service loginAndConnectWithCredentials:credentials dialoutInfo:dialoutInfo completionBlock:^(SoapRequestCompletionBlock completionBlock) {
        request = (SoapRequest*)completionBlock;
        if ([request isKindOfClass: [SoapFault class]]) {                
            return YES; // we got a response, that's all we care about
        }
    return NO;
}
];
return YES;
}

これが私のカテゴリーで、以下に投稿されたものに非常に近いです。

#import "WSAWebSocketAdapterService+BlockExtension.h"

// These objects serve as targets (with action being completed:) for the original object.
// Because we use one for each request we are thread safe.

@interface MyCustomSoapTargetAction : NSObject 

@property (copy) SoapRequestCompletionBlock block;

- (void) completed:(id)sender;

@end

@implementation MyCustomSoapTargetAction
- (void) completed:(id)sender
{
// Assuming 'sender' is your SoapRequest
if (_block != nil)
    _block(sender);
_block = nil;
}
@end

@implementation WSAWebSocketAdapterService(BlockExtension)

- (SoapRequest*) loginAndConnectWithCredentials:(WSAcredentials*) arg0
                                dialoutInfo: (WSAdialoutInfo*) arg1
                            completionBlock:(BOOL (^)(SoapRequestCompletionBlock))completionBlock
{
MyCustomSoapTargetAction *target = [[MyCustomSoapTargetAction alloc] init];
target.block = (SoapRequestCompletionBlock) completionBlock;

//
// Here we assume that target will be retained.
// If that's not the case then we will need to add it to some collection before
// the call below and have the target object remove itself from it after its
// block has been called.
//
return [self loginAndConnect:target action:@selector(completed:) credentials:arg0 dialoutInfo:arg1];
}
@end

助けてくれてありがとう!

4

2 に答える 2

4

実際にはソースコードは必要ありません。カテゴリは問題なく機能します。これは、スレッドセーフでさえあるサンプルコードです。オブジェクトがターゲットを保持していない場合、そのブロックが呼び出されるまでターゲットを保持するために、もう少し作業が必要です。

// -- TheClassName+BlockExtension.h

typedef void (^SoapRequestCompletionBlock)(SoapRequest*);

@interface TheClassName(BlockExtension)
- loginAndConnectWithCredentials:(WSAcredentials*) arg0 
                     dialoutInfo: (WSAdialoutInfo*) arg1;
                 completionBlock:(SoapRequestCompletionBlock)completionBlock;
@end


// -- TheClassName+BlockExtension.m

// EDITED: If the 'target' object is not retained by the loginAndConnect... then
//         we may add it to this collection. In this implementation, a block can
//         only be used once.

static NSMutableSet *gSoapTargets = nil;

// These objects serve as targets (with action being completed:) for the original object.
// Because we use one for each request we are thread safe.

@interface MyCustomSoapTargetAction
@property (copy) SoapRequestCompletionBlock block;
- (void) completed:(id)sender;
@end

@implementation MyCustomSoapTargetAction

- (id) init
{
    // Objects adds itself to the global collection
    if ((self = [super init]))
        [gSoapTargets addObject:self];
    return self;
}

- (void) completed:(id)sender
{
    // Assuming 'sender' is your SoapRequest
    if (_block != nil)
        _block(sender);

    // Object removes itself from global collection when done.
    // On return from this method it will likely be released.
    [gSoapTargets removeObject:self];
}

+ (void) load 
{
    gSoapTargets = [NSMutableSet set];
}

@end

@implementation TheClassName(BlockExtension)
- (SoapRequest*) loginAndConnectWithCredentials:(WSAcredentials*) arg0 
                                    dialoutInfo: (WSAdialoutInfo*) arg1;
                                completionBlock:(SoapRequestCompletionBlock)completionBlock
{
    MyCustomSoapTargetAction *target = [[MyCustomSoapTargetAction alloc] init];
    target.block = completionBlock;

    //
    // Here we assume that target will be retained.
    // If that's not the case then we will need to add it to some collection before
    // the call below and have the target object remove itself from it after its 
    // block has been called.
    //
    [self loginAndConnect:target action:@selector(completed:) credentials:arg0 dialoutInfo:arg1];
}
@end

*更新: コードに基づいてこのカテゴリを使用する例を次に示します。

- (BOOL) serviceIsAvailable 
{
    return _serviceIsAvailable;
}

- (void) _setServiceIsAvailable:(BOOL)value 
{
    // This method should probably be private thus the _ prefix
    // Do something with the result (set some status, warn user...).
    _serviceIsAvailable = value;
}

- (void) checkService 
{
    WSAcredentials* credentials = [[WSAcredentials alloc] init];
    WSAdialoutInfo* dialoutInfo = [[WSAdialoutInfo alloc] init];

    if (_service == nil)
        _service = [[WSAWebSocketAdapterService alloc] init];

    [_service loginAndConnectWithCredentials:credentials 
                                 dialoutInfo:dialoutInfo 
                             completionBlock:^(id sender) 
    {
        [self _setServiceIsAvailable:[sender isKindOfClass:[SoapFault class]]];
    }];
}
于 2013-03-30T12:59:21.573 に答える
0

そのクラスのソースにアクセスできますか? セレクターの代わりにブロックを受け入れる同様のメソッドを生成するのは、かなり単純な書き直しです。

AFNetworking ライブラリは、その種の優れたスタイル ガイドです。メソッドは次のようになります。

+ (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
                                        success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
                                        failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON))failure;

別の方法として、ここに示すアプローチを使用して、カテゴリを介してメソッドを追加することもできます: performSelector:withObject:afterDelay の代わりにブロック:

于 2013-03-29T23:14:54.280 に答える