4

バックグラウンド スレッドの NSOperation 内で非同期 NSURLConnection を実行したいと考えています。これは、データが戻ってきたときに、データに対して非常に高価な操作を行っているためです。

これは、彼らがここで尋ねたことと非常によく似た質問です: How do I do a Asynchronous NSURLConnection inside an NSOperation?

違いは、別のクラスで接続を実行することです。

これが私の最初の試みです:

私のMainViewControllerで:

@property (nonatomic, strong) NSOperationQueue *requestQueue;

#pragma mark - Lazy initialization
- (NSOperationQueue *)requestQueue 
{
    if (!_requestQueue) {
        _requestQueue = [[NSOperationQueue alloc] init];
        _requestQueue.name = @"Request Start Application Queue";
        _requestQueue.maxConcurrentOperationCount = 1;
    }
    return _requestQueue;
}

-(void)callToServer
{
URLJsonRequest *request = [URLRequestFactory createRequest:REQUEST_INTERFACE_CLIENT_VERSION
                                                         delegate:self];

    RequestSender *requestSender = [[RequestSender alloc]initWithPhotoRecord:request delegate:self];

   [self.requestQueue addOperation:requestSender];
}

これが私の操作です:

- (id)initWithPhotoRecord:(URLJsonRequest *)request
                 delegate:(id<RequestSenderDelegate>) theDelegate{

    if (self = [super init])
    {
        self.delegate = theDelegate;
        self.jsonRequest = request;
    }
    return self;
}

- (void)main {

    //Apple recommends using @autoreleasepool block instead of alloc and init NSAutoreleasePool, because blocks are more efficient. You might use NSAuoreleasePool instead and that would be fine.
    @autoreleasepool
    {

        if (self.isCancelled)
            return;

        [self.jsonRequest start];

    }
}

これが私のリクエスト開始関数です:

-(void) start
{
  NSURL *url = [NSURL URLWithString:@"http://google.com"];
 NSURLRequest *theRequest = [NSURLRequest requestWithURL:url];
  urlConnection = [[[NSURLConnection alloc]    initWithRequest:theRequest delegate:self]autorelease];

[urlConnection start];
[theRequest release]
}


- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"Received reponse from connection");
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{



}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection{

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{

}

サーバーから応答がありません。

4

3 に答える 3

2

いくつかのアプローチ:

  1. のパラメーターをNSURLConnection使用して、メインの実行ループで をスケジュールし、実行ループを設定してから、接続を開始する必要があります。次に例を示します。startImmediatelyNO

    urlConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:NO];
    [urlConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [urlConnection start];
    
  2. 接続専用のスレッドを作成し、そのスレッド用に作成した実行ループで接続をスケジュールします。この例については、 AFNetworkingのソースを参照AFURLConnectionOperation.mしてください。

  3. 実際にAFNetworkingを使用NSOperationしてください。これは、キューに追加できるベースの操作を提供し、この実行ループを処理します。


したがって、AFNetworking は次のようなことを行います。

+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"NetworkingThread"];

        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;

    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self
                                                        selector:@selector(networkRequestThreadEntryPoint:)
                                                          object:nil];
        [_networkRequestThread start];
    });

    return _networkRequestThread;
}

だから私は次のようなことをします。まず、いくつかのプライベート プロパティがあります。

@property (nonatomic, readwrite, getter = isExecuting)  BOOL executing;
@property (nonatomic, readwrite, getter = isFinished)   BOOL finished;
@property (nonatomic, weak)   NSURLConnection *connection;

次に、ネットワーク操作で次のようなことができます。

@synthesize executing = _executing;
@synthesize finished  = _finished;

- (instancetype)init {
    self = [super init];
    if (self) {
        _executing = NO;
        _finished = NO;
    }
    return self;
}

- (void)start {
    if (self.isCancelled) {
        [self completeOperation];
        return;
    }

    self.executing = YES;

    [self performSelector:@selector(startInNetworkRequestThread)
                 onThread:[[self class] networkRequestThread]
               withObject:nil
            waitUntilDone:NO];
}

- (void)startInNetworkRequestThread {
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:self.request
                                                                  delegate:self
                                                          startImmediately:NO];
    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    [connection start];

    self.connection = connection;
}

- (void)completeOperation {
    self.executing = NO;
    self.finished = YES;
}

- (void)setFinished:(BOOL)finished {
    if (finished != _finished) {
        [self willChangeValueForKey:@"isFinished"];
        _finished = finished;
        [self didChangeValueForKey:@"isFinished"];
    }
}

- (void)setExecuting:(BOOL)executing {
    if (executing != _executing) {
        [self willChangeValueForKey:@"isExecuting"];
        _executing = executing;
        [self didChangeValueForKey:@"isExecuting"];
    }
}

- (BOOL)isConcurrent {
    return YES;
}

- (BOOL)isAsynchronous {
    return YES;
}

// all of my NSURLConnectionDataDelegate stuff here, for example, upon completion:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    // I call the appropriate completion blocks here, do cleanup, etc. and then, when done:

    [self completeOperation];
}
于 2013-07-02T13:44:31.887 に答える
0

この投稿が 1 年以上前のものであることは承知していますが、独自の非同期ネットワーク操作を作成しようとして同じ問題を抱えている可能性がある人のために、いくつかの提案を追加したいと思います。バックグラウンドで実行される操作に runloop を追加する必要があり、操作が終了したら停止する必要があります。

実際には2つの簡単なオプションがあります:

オプション 1 - NSRunLoop を使用する

NSPort *port        = [NSPort port];
NSRunLoop *runLoop  = [NSRunLoop currentRunLoop];
[runLoop addPort:port forMode:NSDefaultRunLoopMode];
[self.connection scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
[self.connection start];
[runLoop run];

操作が終了したら停止する必要があります。

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
NSDate *date       = [NSDate distantFuture];
while (!runLoopIsStopped && [runLoop runMode:NSDefaultRunLoopMode beforeDate:date]);

オプション 2 - CF を使用

追加する必要があります

CFRunLoopRun();

運用を開始すると

そして電話する

CFRunLoopStop(CFRunLoopGetCurrent());

操作を終了するとき。

次の投稿をお読みください: CFRunLoopRun() vs [NSRunLoop run]

于 2014-06-25T12:01:14.617 に答える