2009-06-12 68 views
5

我想使用UNIX域套接字,与我没有经验Cocoa应用程序来实现IPC。我发现苹果的CFLocalServer示例项目,但它是用C语言编写的外观,不错,相当复杂的(是的,我已阅读大部分)。UNIX域套接字和可可

是在CFLocalServer证实静止状态的最先进的技术,或者是有实现的Objective-C的UNIX域套接字与可可/基金的方式?

我玩NSSocketPort S和NSFileHandle S(其提供一个健康的量抽象的,这对于这个项目是伟大的),并发现了一些非常相关的代码在Mike Bean's Networking in Cocoa,但一直无法使这一切工作呢。

任何人这样做过?

回答

4

最后,我没有使用UNIX域套接字,他们工作得很好。我已经启动了为我的服务器设置套接字(但成功编写了代码,自己创建它),并让客户端连接到它。

事情我了解到:

  • 你可以用你确实需要使用connect()在客户端创建到插座的连接在NSFileHandle

  • 连接的两端

  • 您应该让客户端和服务器都忽略SIGPIPE

  • 如果你用零长度数据回调,这意味着套接字另一端的东西已经断开(即服务器/客户端退出)。确保在这种情况下关闭并释放套接字的末端 - 不要尝试再次读取数据,或者只是获得另一个零长度的读回调(永远和永远)

  • 您应对此负责对于通过套接字界定和组装您发送的邮件(在一个端发送一个消息可能会吐到一个以上的在另一端,或多个消息可能被合并)

我会很高兴分享或发布代码,只是没有时间将其清理公共消费。如果有人有兴趣,请告诉我。

+0

你能分享你的代码吗? – 2012-01-25 11:53:19

0

为什么不试试POSIX命名管道。 Mac OSX版是基于BSD所以它应该是简单的一个POSIX上下的操作系统:

http://www.ecst.csuchico.edu/~beej/guide/ipc/fifos.html

+0

感谢您的回答!据我所知,命名管道有两个端点 - 对于这个项目,我需要一个单服务器和多个客户端的关系。 – s4y 2011-03-08 03:51:47

+0

D e n d l i n k。 – 2012-04-06 16:58:52

6

UNIX域套接字处理您的插座一个很好的教程是一个难啃的劫掠。对于那些还没有这样做,并有兴趣然后去做的人。之后会有成就感。然而,即使从Beej和这个网站的信息,甚至从苹果有很多断开连接。我在这里介绍了一个可以启用ARC的Cocoa的结论性示例。我在等待Sidnicious和他的样品,但从未看到任何东西,所以我决定自己解决这个问题。

什么我这里有三个接口,一个标题和.M实现文件。 超类接口,然后是继承的服务器和客户端接口。 我做了一些有限的测试,它似乎很好地工作。然而,一直在寻找改善,所以请让我知道...

头文件:

typedef enum _CommSocketServerStatus { 

    CommSocketServerStatusUnknown  = 0, 
    CommSocketServerStatusRunning  = 1, 
    CommSocketServerStatusStopped  = 2, 
    CommSocketServerStatusStarting  = 3, 
    CommSocketServerStatusStopping  = 4 

} CommSocketServerStatus; 

typedef enum _CommSocketClientStatus { 

    CommSocketClientStatusUnknown  = 0, 
    CommSocketClientStatusLinked  = 1, 
    CommSocketClientStatusDisconnected = 2, 
    CommSocketClientStatusLinking  = 3, 
    CommSocketClientStatusDisconnecting = 4 

} CommSocketClientStatus; 

@class CommSocketServer, CommSocketClient; 

@protocol CommSocketServerDelegate <NSObject> 
@optional 
- (void) handleSocketServerStopped:(CommSocketServer *)server; 
- (void) handleSocketServerMsgURL:(NSURL *)aURL   fromClient:(CommSocketClient *)client; 
- (void) handleSocketServerMsgString:(NSString *)aString fromClient:(CommSocketClient *)client; 
- (void) handleSocketServerMsgNumber:(NSNumber *)aNumber fromClient:(CommSocketClient *)client; 
- (void) handleSocketServerMsgArray:(NSArray *)aArray fromClient:(CommSocketClient *)client; 
- (void) handleSocketServerMsgDict:(NSDictionary *)aDict fromClient:(CommSocketClient *)client; 
@end 

@protocol CommSocketClientDelegate <NSObject> 
@optional 
- (void) handleSocketClientDisconnect:(CommSocketClient *)client; 
- (void) handleSocketClientMsgURL:(NSURL *)aURL   client:(CommSocketClient *)client; 
- (void) handleSocketClientMsgString:(NSString *)aString client:(CommSocketClient *)client; 
- (void) handleSocketClientMsgNumber:(NSNumber *)aNumber client:(CommSocketClient *)client; 
- (void) handleSocketClientMsgArray:(NSArray *)aArray client:(CommSocketClient *)client; 
- (void) handleSocketClientMsgDict:(NSDictionary *)aDict client:(CommSocketClient *)client; 
@end 

@interface CommSocket : NSObject 
@property (readonly, nonatomic, getter=isSockRefValid) BOOL sockRefValid; 
@property (readonly, nonatomic, getter=isSockConnected) BOOL sockConnected; 
@property (readonly, nonatomic) CFSocketRef sockRef; 
@property (readonly, strong, nonatomic) NSURL *sockURL; 
@property (readonly, strong, nonatomic) NSData *sockAddress; 
@property (readonly, strong, nonatomic) NSString *sockLastError; 
@end 

@interface CommSocketServer : CommSocket <CommSocketClientDelegate> { id <CommSocketServerDelegate> delegate; } 
@property (readwrite, strong, nonatomic) id delegate; 
@property (readonly, strong, nonatomic) NSSet *sockClients; 
@property (readonly, nonatomic) CommSocketServerStatus sockStatus; 
@property (readonly, nonatomic) BOOL startServer; 
@property (readonly, nonatomic) BOOL stopServer; 
- (id) initWithSocketURL:(NSURL *)socketURL; 
+ (id) initAndStartServer:(NSURL *)socketURL; 
- (void) addConnectedClient:(CFSocketNativeHandle)handle; 

- (void) messageClientsURL:(NSURL *)aURL; 
- (void) messageClientsString:(NSString *)aString; 
- (void) messageClientsNumber:(NSNumber *)aNumber; 
- (void) messageClientsArray:(NSArray *)aArray; 
- (void) messageClientsDict:(NSDictionary *)aDict; 

@end 

@interface CommSocketClient : CommSocket { id <CommSocketClientDelegate> delegate; } 
@property (readwrite, strong, nonatomic) id delegate; 
@property (readonly, nonatomic) CommSocketClientStatus sockStatus; 
@property (readonly, nonatomic) CFRunLoopSourceRef sockRLSourceRef; 
@property (readonly, nonatomic) BOOL startClient; 
@property (readonly, nonatomic) BOOL stopClient; 
- (id) initWithSocketURL:(NSURL *)socketURL; 
- (id) initWithSocket:(CFSocketNativeHandle)handle; 
+ (id) initAndStartClient:(NSURL *)socketURL; 
+ (id) initWithSocket:(CFSocketNativeHandle)handle; 

- (void) messageReceived:(NSData *)data; 
- (BOOL) messageURL:(NSURL *)aURL; 
- (BOOL) messageString:(NSString *)aString; 
- (BOOL) messageNumber:(NSNumber *)aNumber; 
- (BOOL) messageArray:(NSArray *)aArray; 
- (BOOL) messageDict:(NSDictionary *)aDict; 

@end 

实现文件:(我将介绍三个部分)

第一节(超类)

#import "CommSocket.h" 

#import <sys/un.h> 
#import <sys/socket.h> 

#pragma mark Socket Superclass: 

@interface CommSocket() 
@property (readwrite, nonatomic) CFSocketRef sockRef; 
@property (readwrite, strong, nonatomic) NSURL *sockURL; 
@end 

@implementation CommSocket 
@synthesize sockConnected; 
@synthesize sockRef, sockURL; 

- (BOOL) isSockRefValid { 
    if (self.sockRef == nil) return NO; 
    return (BOOL)CFSocketIsValid(self.sockRef); 
} 

- (NSData *) sockAddress { 

    struct sockaddr_un address; 
    address.sun_family = AF_UNIX; 
    strcpy(address.sun_path, [[self.sockURL path] fileSystemRepresentation]); 
    address.sun_len = SUN_LEN(&address); 
    return [NSData dataWithBytes:&address length:sizeof(struct sockaddr_un)]; 
} 

- (NSString *) sockLastError { 
    return [NSString stringWithFormat:@"%s (%d)", strerror(errno), errno ]; 
} 

@end 

第二节(服务器)

注:服务器雷乌斯es连接到自己的客户端的客户端代码。 面向对象编程,一定要喜欢它!

#pragma mark - Socket: Server 
#pragma mark - 

@interface CommSocketServer() 
@property (readonly, nonatomic) BOOL startServerCleanup; 
@property (readwrite, nonatomic) CommSocketServerStatus sockStatus; 
@property (readwrite, strong, nonatomic) NSSet *sockClients; 
static void SocketServerCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); 
@end 

#pragma mark - Server Implementation: 

@implementation CommSocketServer 

@synthesize delegate; 
@synthesize sockStatus; 
@synthesize sockClients; 

#pragma mark - Helper Methods: 

- (BOOL) socketServerCreate { 

    if (self.sockRef != nil) return NO; 
    CFSocketNativeHandle sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    CFSocketContext context = { 0, (__bridge void *)self, nil, nil, nil }; 
    CFSocketRef refSock = CFSocketCreateWithNative(nil, sock, kCFSocketAcceptCallBack, SocketServerCallback, &context); 

    if (refSock == nil) return NO; 

    int opt = 1; 
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)); 
    setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt)); 

    self.sockRef = refSock; 
    CFRelease(refSock); 

    return YES; 
} 

- (BOOL) socketServerBind { 
    if (self.sockRef == nil) return NO; 
    unlink([[self.sockURL path] fileSystemRepresentation]); 
    if (CFSocketSetAddress(self.sockRef, (__bridge CFDataRef)self.sockAddress) != kCFSocketSuccess) return NO; 
    return YES; 
} 

#pragma mark - Connected Clients: 

- (void) disconnectClients { 


    for (CommSocketClient *client in self.sockClients) 
     [client stopClient]; 

    self.sockClients = [NSSet set]; 
} 

- (void) disconnectClient:(CommSocketClient *)client { 

    @synchronized(self) { 
     NSMutableSet *clients = [NSMutableSet setWithSet:self.sockClients]; 

     if ([clients containsObject:client]) { 

      if (client.isSockRefValid) [client stopClient]; 
      [clients removeObject:client]; 
      self.sockClients = clients; 
    } } 
} 

- (void) addConnectedClient:(CFSocketNativeHandle)handle { 

    @synchronized(self) { 
     CommSocketClient *client = [CommSocketClient initWithSocket:handle]; 
     client.delegate = self; 
     NSMutableSet *clients = [NSMutableSet setWithSet:self.sockClients]; 

     if (client.isSockConnected) { 
      [clients addObject:client]; 
      self.sockClients = clients; 
    } } 
} 

#pragma mark - Connected Client Protocols: 

- (void) handleSocketClientDisconnect:(CommSocketClient *)client { 

    [self disconnectClient:client]; 
} 

- (void) handleSocketClientMsgURL:(NSURL *)aURL client:(CommSocketClient *)client { 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerMsgURL:server:fromClient:)]) 
     [self.delegate handleSocketServerMsgURL:aURL fromClient:client]; 
} 

- (void) handleSocketClientMsgString:(NSString *)aString client:(CommSocketClient *)client { 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerMsgString:fromClient:)]) 
     [self.delegate handleSocketServerMsgString:aString fromClient:client]; 
} 

- (void) handleSocketClientMsgNumber:(NSNumber *)aNumber client:(CommSocketClient *)client { 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerMsgNumber:fromClient:)]) 
     [self.delegate handleSocketClientMsgNumber:aNumber client:client]; 
} 

- (void) handleSocketClientMsgArray:(NSArray *)aArray client:(CommSocketClient *)client { 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerMsgArray:fromClient:)]) 
     [self.delegate handleSocketServerMsgArray:aArray fromClient:client]; 
} 

- (void) handleSocketClientMsgDict:(NSDictionary *)aDict client:(CommSocketClient *)client { 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerMsgDict:fromClient:)]) 
     [self.delegate handleSocketServerMsgDict:aDict fromClient:client]; 
} 

#pragma mark - Connected Client Messaging: 

- (void) messageClientsURL:(NSURL *)aURL { 
    for (CommSocketClient *client in self.sockClients) 
     [client messageURL:aURL]; 
} 

- (void) messageClientsString:(NSString *)aString { 
    for (CommSocketClient *client in self.sockClients) 
     [client messageString:aString]; 
} 

- (void) messageClientsNumber:(NSNumber *)aNumber { 
    for (CommSocketClient *client in self.sockClients) 
     [client messageNumber:aNumber]; 
} 

- (void) messageClientsArray:(NSArray *)aArray { 
    for (CommSocketClient *client in self.sockClients) 
     [client messageArray:aArray]; 
} 

- (void) messageClientsDict:(NSDictionary *)aDict { 
    for (CommSocketClient *client in self.sockClients) 
     [client messageDict:aDict]; 
} 

#pragma mark - Start/Stop Server: 

- (BOOL) startServerCleanup { [self stopServer]; return NO; } 

- (BOOL) startServer { 

    if (self.sockStatus == CommSocketServerStatusRunning) return YES; 
    self.sockStatus = CommSocketServerStatusStarting; 

    if (![self socketServerCreate]) return self.startServerCleanup; 
    if (![self socketServerBind] ) return self.startServerCleanup; 

    CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.sockRef, 0); 
    CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes); 
    CFRelease(sourceRef); 

    self.sockStatus = CommSocketServerStatusRunning; 
    return YES; 
} 

- (BOOL) stopServer { 

    self.sockStatus = CommSocketServerStatusStopping; 

    [self disconnectClients]; 

    if (self.sockRef != nil) { 

     CFSocketInvalidate(self.sockRef); 
     self.sockRef = nil; 
    } 

    unlink([[self.sockURL path] fileSystemRepresentation]); 

    if ([self.delegate respondsToSelector:@selector(handleSocketServerStopped:)]) 
     [self.delegate handleSocketServerStopped:self]; 

    self.sockStatus = CommSocketServerStatusStopped; 
    return YES; 
} 

#pragma mark - Server Validation: 

- (BOOL) isSockConnected { 

    if (self.sockStatus == CommSocketServerStatusRunning) 
     return self.isSockRefValid; 

    return NO; 
} 

#pragma mark - Initialization: 

+ (id) initAndStartServer:(NSURL *)socketURL { 

    CommSocketServer *server = [[CommSocketServer alloc] initWithSocketURL:socketURL]; 
    [server startServer]; 
    return server; 
} 

- (id) initWithSocketURL:(NSURL *)socketURL { 

    if ((self = [super init])) { 

     self.sockURL  = socketURL; 
     self.sockStatus = CommSocketServerStatusStopped; 
     self.sockClients = [NSSet set]; 

    } return self; 
} 

- (void) dealloc { [self stopServer]; } 

#pragma mark - Server Callback: 

static void SocketServerCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { 

    CommSocketServer *server = (__bridge CommSocketServer *)info; 

    if (kCFSocketAcceptCallBack == type) { 
     CFSocketNativeHandle handle = *(CFSocketNativeHandle *)data; 
     [server addConnectedClient:handle]; 
    } 
} 

@end 

第三节(客户端)

#pragma mark - Socket: Client 
#pragma mark - 

@interface CommSocketClient() 
@property (readonly, nonatomic) BOOL startClientCleanup; 
@property (readwrite, nonatomic) CommSocketClientStatus sockStatus; 
@property (readwrite, nonatomic) CFRunLoopSourceRef sockRLSourceRef; 
static void SocketClientCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info); 
@end 

#pragma mark - Client Implementation: 

@implementation CommSocketClient 

static NSTimeInterval const kCommSocketClientTimeout = 5.0; 

@synthesize delegate; 
@synthesize sockStatus; 
@synthesize sockRLSourceRef; 

#pragma mark - Helper Methods: 

- (BOOL) socketClientCreate:(CFSocketNativeHandle)sock { 

    if (self.sockRef != nil) return NO; 
    CFSocketContext context = { 0, (__bridge void *)self, nil, nil, nil }; 
    CFSocketCallBackType types = kCFSocketDataCallBack; 
    CFSocketRef refSock = CFSocketCreateWithNative(nil, sock, types, SocketClientCallback, &context); 

    if (refSock == nil) return NO; 

    int opt = 1; 
    setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&opt, sizeof(opt)); 

    self.sockRef = refSock; 
    CFRelease(refSock); 

    return YES; 
} 

- (BOOL) socketClientBind { 
    if (self.sockRef == nil) return NO; 
    if (CFSocketConnectToAddress(self.sockRef, 
            (__bridge CFDataRef)self.sockAddress, 
            (CFTimeInterval)kCommSocketClientTimeout) != kCFSocketSuccess) return NO; 
    return YES; 
} 

#pragma mark - Client Messaging: 

- (void) messageReceived:(NSData *)data { 

    id msg = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 

    if ([msg isKindOfClass:[NSURL class]]) { 

     if ([self.delegate respondsToSelector:@selector(handleSocketClientMsgURL:client:)]) 
      [self.delegate handleSocketClientMsgURL:(NSURL *)msg client:self]; 
    } 

    else if ([msg isKindOfClass:[NSString class]]) { 

     if ([self.delegate respondsToSelector:@selector(handleSocketClientMsgString:client:)]) 
      [self.delegate handleSocketClientMsgString:(NSString *)msg client:self]; 
    } 

    else if ([msg isKindOfClass:[NSNumber class]]) { 

     if ([self.delegate respondsToSelector:@selector(handleSocketClientMsgNumber:client:)]) 
      [self.delegate handleSocketClientMsgNumber:(NSNumber *)msg client:self]; 
    } 

    else if ([msg isKindOfClass:[NSArray class]]) { 

     if ([self.delegate respondsToSelector:@selector(handleSocketClientMsgArray:client:)]) 
      [self.delegate handleSocketClientMsgArray:(NSArray *)msg client:self]; 
    } 

    else if ([msg isKindOfClass:[NSDictionary class]]) { 

     if ([self.delegate respondsToSelector:@selector(handleSocketClientMsgDict:client:)]) 
      [self.delegate handleSocketClientMsgDict:(NSDictionary *)msg client:self]; 
    } 
} 

- (BOOL) messageData:(NSData *)data { 

    if (self.isSockConnected) { 

     if (kCFSocketSuccess == CFSocketSendData(self.sockRef, 
                nil, 
                (__bridge CFDataRef)data, 
                kCommSocketClientTimeout)) 
      return YES; 

    } return NO; 
} 

- (BOOL) messageURL:(NSURL *)aURL   { return [self messageData:[NSKeyedArchiver archivedDataWithRootObject:aURL]]; } 
- (BOOL) messageString:(NSString *)aString { return [self messageData:[NSKeyedArchiver archivedDataWithRootObject:aString]]; } 
- (BOOL) messageNumber:(NSNumber *)aNumber { return [self messageData:[NSKeyedArchiver archivedDataWithRootObject:aNumber]]; } 
- (BOOL) messageArray:(NSArray *)aArray { return [self messageData:[NSKeyedArchiver archivedDataWithRootObject:aArray]]; } 
- (BOOL) messageDict:(NSDictionary *)aDict { return [self messageData:[NSKeyedArchiver archivedDataWithRootObject:aDict]]; } 

#pragma mark - Start/Stop Client: 

- (BOOL) startClientCleanup { [self stopClient]; return NO; } 

- (BOOL) startClient { 

    if (self.sockStatus == CommSocketClientStatusLinked) return YES; 
    self.sockStatus = CommSocketClientStatusLinking; 

    CFSocketNativeHandle sock = socket(AF_UNIX, SOCK_STREAM, 0); 
    if (![self socketClientCreate:sock]) return self.startClientCleanup; 
    if (![self socketClientBind]  ) return self.startClientCleanup; 

    CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.sockRef, 0); 
    CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes); 

    self.sockRLSourceRef = sourceRef; 
    CFRelease(sourceRef); 

    self.sockStatus = CommSocketClientStatusLinked; 
    return YES; 
} 

- (BOOL) stopClient { 

    self.sockStatus = CommSocketClientStatusDisconnecting; 

    if (self.sockRef != nil) { 

     if (self.sockRLSourceRef != nil) { 

      CFRunLoopSourceInvalidate(self.sockRLSourceRef); 
      self.sockRLSourceRef = nil; 
     } 

     CFSocketInvalidate(self.sockRef); 
     self.sockRef = nil; 
    } 

    if ([self.delegate respondsToSelector:@selector(handleSocketClientDisconnect:)]) 
     [self.delegate handleSocketClientDisconnect:self]; 

    self.sockStatus = CommSocketClientStatusDisconnected; 

    return YES; 
} 

#pragma mark - Client Validation: 

- (BOOL) isSockConnected { 

    if (self.sockStatus == CommSocketClientStatusLinked) 
     return self.isSockRefValid; 

    return NO; 
} 

#pragma mark - Initialization: 

+ (id) initAndStartClient:(NSURL *)socketURL { 

    CommSocketClient *client = [[CommSocketClient alloc] initWithSocketURL:socketURL]; 
    [client startClient]; 
    return client; 
} 

+ (id) initWithSocket:(CFSocketNativeHandle)handle { 

    CommSocketClient *client = [[CommSocketClient alloc] initWithSocket:handle]; 
    return client; 
} 

- (id) initWithSocketURL:(NSURL *)socketURL { 

    if ((self = [super init])) { 

     self.sockURL = socketURL; 
     self.sockStatus = CommSocketClientStatusDisconnected; 

    } return self; 
} 

- (id) initWithSocket:(CFSocketNativeHandle)handle { 

    if ((self = [super init])) { 

     self.sockStatus = CommSocketClientStatusLinking; 

     if (![self socketClientCreate:handle]) [self startClientCleanup]; 

     else { 

      CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, self.sockRef, 0); 
      CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes); 

      self.sockRLSourceRef = sourceRef; 
      CFRelease(sourceRef); 

      self.sockStatus = CommSocketClientStatusLinked; 
     } 

    } return self; 
} 

- (void) dealloc { [self stopClient]; } 

#pragma mark - Client Callback: 

static void SocketClientCallback (CFSocketRef sock, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { 

    CommSocketClient *client = (__bridge CommSocketClient *)info; 

    if (kCFSocketDataCallBack == type) { 

     NSData *objData = (__bridge NSData *)data; 

     if ([objData length] == 0) 
      [client stopClient]; 

     else 
      [client messageReceived:objData]; 
    } 
} 

@end 

好吧,这就是它,它应该来自不同进程的工作。

只需使用此命令创建一个服务器,其中包含放置套接字文件的路径。

  • (id)initAndStartServer:(NSURL *)socketURL;

同样地,用它来创建客户端连接

  • (ID)initAndStartClient:(NSURL *)socketURL;

其余的应该很简单,包括我所包含的委托方法。 最后,保持url路径很小(没有添加任何真正的检查) ,你可以在单独的NSOperationQueue中实例化这些(未经测试)。

希望这可以帮助那里的人作为一个完整的工作示例。 Arvin