【问题标题】:NSOperationQueue with loop and class with delegates带有循环的 NSOperationQueue 和带有委托的类
【发布时间】:2015-07-06 20:16:45
【问题描述】:

我是 NSOperationQueue 的新手,我正在尝试创建一个可以 ping 我网络中的所有主机的设备。 首先,我使用的是 Apple 的 SimplePing.h 类,它一次只 ping 一个主机。 这个类有一些委托来通知主类 ping 是否成功。 现在就我而言,我想将所有主机从192.168.1.1 ping 到192.168.1.254,所以这是我的代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // Create a new NSOperationQueue instance.
    operationQueue = [NSOperationQueue new];



    for (int i=1; i<254; i++) {

        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                                selector:@selector(pingHost:)
                                                                                  object:[NSString stringWithFormat:@"192.168.1.%d",i]];
        // Add the operation to the queue and let it to be executed.
        [operationQueue addOperation:operation];


    }


}

-(void)pingHost:(NSString*)ip{

    ping = [SimplePing simplePingWithHostName:ip];  
    self.ping.delegate=self;
    [ping start];


};
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// When the pinger starts, send the ping immediately
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address {


    [pinger sendPingWithData:nil];
}

- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet{


    [self performSelector:@selector(didNotReceivedAnswer) withObject:nil afterDelay:2];

    NSLog(@"didsendpacket");

}
- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error {
    NSLog(@"didFail");

    [self didNotReceivedAnswer];

}

- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet error:(NSError *)error {
    NSLog(@"didfailtosendpacket");

    [self didNotReceivedAnswer];

}

- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet {

    NSLog(@"didreceivesresponse");

}

- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet{

    [self didNotReceivedAnswer];
    NSLog(@"didreceiveunexpected");

}

//Helper for delegate
-(void)didNotReceivedAnswer{
}

然后我得到这个错误:

断言失败:(self->_host == NULL),函数 -[SimplePing start], 文件 /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, 第 574 行。断言失败:(self->_host == NULL),函数 -[SimplePing 开始],文件 /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, 第 574 行。断言失败:(self->_host == NULL),函数 -[SimplePing 开始],文件 /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, 第 574 行。断言失败:(self->_host == NULL),函数 -[SimplePing 开始],文件 /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, 第 574 行。断言失败:(self->_host == NULL),函数 -[SimplePing 开始],文件 /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, 第 574 行。

有什么想法吗?

【问题讨论】:

  • 我猜你需要在格式字符串中使用%i 而不是%d
  • 不,这不是我的问题。我刚刚检查过了。它必须与SimplePing 和 ARC 相关

标签: ios xcode multithreading nsoperation nsoperationqueue


【解决方案1】:

您的问题是您必须保留对每个 ping 对象的引用。到目前为止,您已将 ping 声明为类变量,因此每次调用 pingHost 时,它都会被重新初始化,而前一个会丢失。这就是你得到(self-&gt;_host == NULL) 断言失败的原因。

您需要使用NSOperationQueue 和子类NSOperation 来处理此问题。拥有NSOperation 将允许NSOperation 的每个实例化保留其唯一的ping 对象。

我很快就完成了,所以你可以解决它。理想情况下,您希望为 PingOperation 类创建一个委托,以便您可以从中获取对主线程的回调。

PingOperation.h

#import <Foundation/Foundation.h>

@interface PingOpertion : NSOperation

-(id)initWithHostName:(NSString*)hostName;

@end

PingOperation.m

#import "PingOpertion.h"
#include "SimplePing.h"

#include <sys/socket.h>
#include <netdb.h>

static NSString * DisplayAddressForAddress(NSData * address)
// Returns a dotted decimal string for the specified address (a (struct sockaddr)
// within the address NSData).
{
    int         err;
    NSString *  result;
    char        hostStr[NI_MAXHOST];

    result = nil;

    if (address != nil) {
        err = getnameinfo([address bytes], (socklen_t) [address length], hostStr, sizeof(hostStr), NULL, 0, NI_NUMERICHOST);
        if (err == 0) {
            result = [NSString stringWithCString:hostStr encoding:NSASCIIStringEncoding];
            assert(result != nil);
        }
    }

    return result;
}

@interface PingOpertion () <SimplePingDelegate>

@property NSString *hostName;

@property (nonatomic, strong, readwrite) SimplePing *   pinger;
@property (nonatomic, strong, readwrite) NSTimer *      sendTimer;
@end


@implementation PingOpertion

@synthesize pinger    = _pinger;
@synthesize sendTimer = _sendTimer;


-(id)initWithHostName:(NSString*)hostName {
    if (self = [super init]) {
        self.hostName = hostName;
    }
    return self;
}

- (void)main {
    // a lengthy operation
    @autoreleasepool {
        assert(self.pinger == nil);

        self.pinger = [SimplePing simplePingWithHostName:self.hostName];
        assert(self.pinger != nil);

        self.pinger.delegate = self;
        [self.pinger start];

        do {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        } while (self.pinger != nil);
    }
}


- (NSString *)shortErrorFromError:(NSError *)error
// Given an NSError, returns a short error string that we can print, handling
// some special cases along the way.
{
    NSString *      result;
    NSNumber *      failureNum;
    int             failure;
    const char *    failureStr;

    assert(error != nil);

    result = nil;

    // Handle DNS errors as a special case.

    if ( [[error domain] isEqual:(NSString *)kCFErrorDomainCFNetwork] && ([error code] == kCFHostErrorUnknown) ) {
        failureNum = [[error userInfo] objectForKey:(id)kCFGetAddrInfoFailureKey];
        if ( [failureNum isKindOfClass:[NSNumber class]] ) {
            failure = [failureNum intValue];
            if (failure != 0) {
                failureStr = gai_strerror(failure);
                if (failureStr != NULL) {
                    result = [NSString stringWithUTF8String:failureStr];
                    assert(result != nil);
                }
            }
        }
    }

    // Otherwise try various properties of the error object.

    if (result == nil) {
        result = [error localizedFailureReason];
    }
    if (result == nil) {
        result = [error localizedDescription];
    }
    if (result == nil) {
        result = [error description];
    }
    assert(result != nil);
    return result;
}

- (void)runWithHostName:(NSString *)hostName
// The Objective-C 'main' for this program.  It creates a SimplePing object
// and runs the runloop sending pings and printing the results.
{
    assert(self.pinger == nil);

    self.pinger = [SimplePing simplePingWithHostName:hostName];
    assert(self.pinger != nil);

    self.pinger.delegate = self;
    [self.pinger start];

    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    } while (self.pinger != nil);
}

- (void)sendPing
// Called to send a ping, both directly (as soon as the SimplePing object starts up)
// and via a timer (to continue sending pings periodically).
{
    assert(self.pinger != nil);
    [self.pinger sendPingWithData:nil];
}

- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address
// A SimplePing delegate callback method.  We respond to the startup by sending a
// ping immediately and starting a timer to continue sending them every second.
{
#pragma unused(pinger)
    assert(pinger == self.pinger);
    assert(address != nil);

    NSLog(@"pinging %@", DisplayAddressForAddress(address));

    // Send the first ping straight away.

    [self sendPing];

    // And start a timer to send the subsequent pings.

    assert(self.sendTimer == nil);
    self.sendTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(sendPing) userInfo:nil repeats:YES];
}

- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error
// A SimplePing delegate callback method.  We shut down our timer and the
// SimplePing object itself, which causes the runloop code to exit.
{
#pragma unused(pinger)
    assert(pinger == self.pinger);
#pragma unused(error)
    NSLog(@"failed: %@", [self shortErrorFromError:error]);

    [self.sendTimer invalidate];
    self.sendTimer = nil;

    // No need to call -stop.  The pinger will stop itself in this case.
    // We do however want to nil out pinger so that the runloop stops.

    self.pinger = nil;
}

- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet
// A SimplePing delegate callback method.  We just log the send.
{
#pragma unused(pinger)
    assert(pinger == self.pinger);
#pragma unused(packet)
    NSLog(@"#%u sent", (unsigned int) OSSwapBigToHostInt16(((const ICMPHeader *) [packet bytes])->sequenceNumber) );
}

- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet error:(NSError *)error
// A SimplePing delegate callback method.  We just log the failure.
{
#pragma unused(pinger)
    assert(pinger == self.pinger);
#pragma unused(packet)
#pragma unused(error)
    NSLog(@"#%u send failed: %@", (unsigned int) OSSwapBigToHostInt16(((const ICMPHeader *) [packet bytes])->sequenceNumber), [self shortErrorFromError:error]);
}

- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet
// A SimplePing delegate callback method.  We just log the reception of a ping response.
{
#pragma unused(pinger)
    assert(pinger == self.pinger);
#pragma unused(packet)
    NSLog(@"#%u received", (unsigned int) OSSwapBigToHostInt16([SimplePing icmpInPacket:packet]->sequenceNumber) );
}

- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet
// A SimplePing delegate callback method.  We just log the receive.
{
    const ICMPHeader *  icmpPtr;

#pragma unused(pinger)
    assert(pinger == self.pinger);
#pragma unused(packet)

    icmpPtr = [SimplePing icmpInPacket:packet];
    if (icmpPtr != NULL) {
        NSLog(@"#%u unexpected ICMP type=%u, code=%u, identifier=%u", (unsigned int) OSSwapBigToHostInt16(icmpPtr->sequenceNumber), (unsigned int) icmpPtr->type, (unsigned int) icmpPtr->code, (unsigned int) OSSwapBigToHostInt16(icmpPtr->identifier) );
    } else {
        NSLog(@"unexpected packet size=%zu", (size_t) [packet length]);
    }
}

@end

现在,当您想开始 ping 某些人时,您只需通过 NSOperationQueue 进行管理即可。

例子

self.pingQueue = [[NSOperationQueue alloc] init];


    NSArray *host = [NSArray arrayWithObjects:@"http://www.google.com", @"http://www.stackoverflow.com", @"http://www.woot.com", nil];

    for (int i = 0; i < host.count; i++) {
        PingOpertion *pingOperation = [[PingOpertion alloc] initWithHostName:host[i]];
        [self.pingQueue addOperation:pingOperation];
    }

【讨论】:

  • 好像所有的操作都没有执行!如何确保所有操作都执行完毕?
  • 您可以使用 KVO 监控剩余的操作数。你可以观察NSOperationQueueoperationCount属性
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-03
  • 2016-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
  • 1970-01-01
相关资源
最近更新 更多