带循环的 NSOperationQueue 和带委托的 class
NSOperationQueue with loop and class with delegates
我是 NSOperationQueue 的新手,我正在尝试创建一个可以对我网络中的所有主机执行 ping 操作的队列。
首先,我使用的是 Apple 的 class SimplePing.h
,它一次只对一台主机执行 ping 操作。
这个 class 有一些代表通知主 class ping 是否成功。
现在在我的例子中,我想 ping 从 192.168.1.1
到 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{
}
然后我得到这个错误:
Assertion failed: (self->_host == NULL), function -[SimplePing start],
file
/Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m,
line 574. Assertion failed: (self->_host == NULL), function
-[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m,
line 574. Assertion failed: (self->_host == NULL), function
-[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m,
line 574. Assertion failed: (self->_host == NULL), function
-[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m,
line 574. Assertion failed: (self->_host == NULL), function
-[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m,
line 574.
知道这里出了什么问题吗?
您的问题是您必须保留对每个 ping
对象的引用。截至目前,您已将 ping
声明为 class 变量,因此每次调用 pingHost
时,它都会被重新初始化,而前一个会丢失。这就是为什么您会收到 (self->_host == NULL)
断言失败的原因。
您需要使用 NSOperationQueue
和子classed NSOperation
来处理这个问题。拥有 NSOperation
将允许 NSOperation
的每个实例化保留其唯一的 ping
对象。
我很快就完成了这个,所以你可以解决它。理想情况下,您希望为 PingOperation
class 创建一个委托,这样您就可以从中获得对主线程的回调。
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.whosebug.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];
}
我是 NSOperationQueue 的新手,我正在尝试创建一个可以对我网络中的所有主机执行 ping 操作的队列。
首先,我使用的是 Apple 的 class SimplePing.h
,它一次只对一台主机执行 ping 操作。
这个 class 有一些代表通知主 class ping 是否成功。
现在在我的例子中,我想 ping 从 192.168.1.1
到 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{
}
然后我得到这个错误:
Assertion failed: (self->_host == NULL), function -[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, line 574. Assertion failed: (self->_host == NULL), function -[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, line 574. Assertion failed: (self->_host == NULL), function -[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, line 574. Assertion failed: (self->_host == NULL), function -[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, line 574. Assertion failed: (self->_host == NULL), function -[SimplePing start], file /Users/Mike/Documents/Xcode/Unused/PingTest/PingTest/SimplePing.m, line 574.
知道这里出了什么问题吗?
您的问题是您必须保留对每个 ping
对象的引用。截至目前,您已将 ping
声明为 class 变量,因此每次调用 pingHost
时,它都会被重新初始化,而前一个会丢失。这就是为什么您会收到 (self->_host == NULL)
断言失败的原因。
您需要使用 NSOperationQueue
和子classed NSOperation
来处理这个问题。拥有 NSOperation
将允许 NSOperation
的每个实例化保留其唯一的 ping
对象。
我很快就完成了这个,所以你可以解决它。理想情况下,您希望为 PingOperation
class 创建一个委托,这样您就可以从中获得对主线程的回调。
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.whosebug.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];
}