mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2026-04-24 00:00:04 +08:00
#35 First pass at unit tests
This commit is contained in:
@@ -79,7 +79,9 @@ NSString* GCDWebServerGetPrimaryIPv4Address(); // Returns IPv4 address of prima
|
||||
@property(nonatomic, readonly) NSURL* serverURL; // Only non-nil if server is running
|
||||
@property(nonatomic, readonly) NSURL* bonjourServerURL; // Only non-nil if server is running and Bonjour registration is active
|
||||
#if !TARGET_OS_IPHONE
|
||||
@property(nonatomic, getter=isRecordingEnabled) BOOL recordingEnabled; // Creates files in the current directory containing the raw data for all requests and responses (directory most NOT contain prior recordings)
|
||||
- (BOOL)runWithPort:(NSUInteger)port; // Starts then automatically stops on SIGINT i.e. Ctrl-C (use on main thread only)
|
||||
- (NSInteger)runTestsInDirectory:(NSString*)path withPort:(NSUInteger)port; // Returns number of failed tests or -1 if server failed to start
|
||||
#endif
|
||||
@end
|
||||
|
||||
|
||||
+166
-3
@@ -53,6 +53,9 @@
|
||||
NSUInteger _port;
|
||||
dispatch_source_t _source;
|
||||
CFNetServiceRef _service;
|
||||
#if !TARGET_OS_IPHONE
|
||||
BOOL _recording;
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -152,9 +155,13 @@ NSDate* GCDWebServerParseHTTPDate(NSString* string) {
|
||||
return date;
|
||||
}
|
||||
|
||||
NSString* GCDWebServerDescribeData(NSData* data, NSString* contentType) {
|
||||
if ([contentType hasPrefix:@"text/"] || [contentType isEqualToString:@"application/json"] || [contentType isEqualToString:@"application/xml"]) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(contentType, @"charset");
|
||||
static inline BOOL _IsTextContentType(NSString* type) {
|
||||
return ([type hasPrefix:@"text/"] || [type hasPrefix:@"application/json"] || [type hasPrefix:@"application/xml"]);
|
||||
}
|
||||
|
||||
NSString* GCDWebServerDescribeData(NSData* data, NSString* type) {
|
||||
if (_IsTextContentType(type)) {
|
||||
NSString* charset = GCDWebServerExtractHeaderValueParameter(type, @"charset");
|
||||
NSString* string = [[NSString alloc] initWithData:data encoding:GCDWebServerStringEncodingFromCharset(charset)];
|
||||
if (string) {
|
||||
return ARC_AUTORELEASE(string);
|
||||
@@ -529,6 +536,18 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
||||
|
||||
@implementation GCDWebServer (Extensions)
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
|
||||
- (void)setRecordingEnabled:(BOOL)flag {
|
||||
_recording = flag;
|
||||
}
|
||||
|
||||
- (BOOL)isRecordingEnabled {
|
||||
return _recording;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
- (NSURL*)serverURL {
|
||||
if (_source) {
|
||||
NSString* ipAddress = GCDWebServerGetPrimaryIPv4Address();
|
||||
@@ -576,6 +595,150 @@ static void _NetServiceClientCallBack(CFNetServiceRef service, CFStreamError* er
|
||||
return success;
|
||||
}
|
||||
|
||||
static CFHTTPMessageRef _CreateHTTPMessageFromFileDump(NSString* path, BOOL isRequest) {
|
||||
NSData* data = [NSData dataWithContentsOfFile:path];
|
||||
if (data) {
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, isRequest);
|
||||
if (CFHTTPMessageAppendBytes(message, data.bytes, data.length)) {
|
||||
return message;
|
||||
}
|
||||
CFRelease(message);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CFHTTPMessageRef _CreateHTTPMessageFromHTTPRequestResponse(CFHTTPMessageRef request) {
|
||||
CFHTTPMessageRef response = NULL;
|
||||
CFReadStreamRef stream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
|
||||
if (CFReadStreamOpen(stream)) {
|
||||
CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
CFDataSetLength(data, 256 * 1024);
|
||||
CFIndex length = 0;
|
||||
while (1) {
|
||||
CFIndex result = CFReadStreamRead(stream, CFDataGetMutableBytePtr(data) + length, CFDataGetLength(data) - length);
|
||||
if (result <= 0) {
|
||||
break;
|
||||
}
|
||||
length += result;
|
||||
if (length >= CFDataGetLength(data)) {
|
||||
CFDataSetLength(data, 2 * CFDataGetLength(data));
|
||||
}
|
||||
}
|
||||
if (CFReadStreamGetStatus(stream) == kCFStreamStatusAtEnd) {
|
||||
response = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
||||
if (response) {
|
||||
CFDataSetLength(data, length);
|
||||
CFHTTPMessageSetBody(response, data);
|
||||
}
|
||||
}
|
||||
CFRelease(data);
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
static void _LogResult(NSString* format, ...) {
|
||||
va_list arguments;
|
||||
va_start(arguments, format);
|
||||
NSString* message = [[NSString alloc] initWithFormat:format arguments:arguments];
|
||||
va_end(arguments);
|
||||
fprintf(stdout, "%s\n", [message UTF8String]);
|
||||
ARC_RELEASE(message);
|
||||
}
|
||||
|
||||
- (NSInteger)runTestsInDirectory:(NSString*)path withPort:(NSUInteger)port {
|
||||
NSInteger result = -1;
|
||||
if ([self startWithPort:port bonjourName:nil]) {
|
||||
|
||||
result = 0;
|
||||
NSArray* files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
|
||||
for (NSString* requestFile in files) {
|
||||
if (![requestFile hasSuffix:@".request"]) {
|
||||
continue;
|
||||
}
|
||||
@autoreleasepool {
|
||||
NSString* index = [[requestFile componentsSeparatedByString:@"-"] firstObject];
|
||||
BOOL success = NO;
|
||||
CFHTTPMessageRef request = _CreateHTTPMessageFromFileDump([path stringByAppendingPathComponent:requestFile], YES);
|
||||
if (request) {
|
||||
_LogResult(@"[%i] %@ %@", (int)[index integerValue], ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestMethod(request)), [ARC_BRIDGE_RELEASE(CFHTTPMessageCopyRequestURL(request)) path]);
|
||||
NSString* prefix = [index stringByAppendingString:@"-"];
|
||||
for (NSString* responseFile in files) {
|
||||
if ([responseFile hasPrefix:prefix] && [responseFile hasSuffix:@".response"]) {
|
||||
CFHTTPMessageRef expectedResponse = _CreateHTTPMessageFromFileDump([path stringByAppendingPathComponent:responseFile], NO);
|
||||
if (expectedResponse) {
|
||||
CFHTTPMessageRef actualResponse = _CreateHTTPMessageFromHTTPRequestResponse(request);
|
||||
if (actualResponse) {
|
||||
success = YES;
|
||||
|
||||
CFIndex expectedStatusCode = CFHTTPMessageGetResponseStatusCode(expectedResponse);
|
||||
CFIndex actualStatusCode = CFHTTPMessageGetResponseStatusCode(actualResponse);
|
||||
if (actualStatusCode != expectedStatusCode) {
|
||||
_LogResult(@" Status code not matching:\n Expected: %i\n Actual: %i", (int)expectedStatusCode, (int)actualStatusCode);
|
||||
success = NO;
|
||||
}
|
||||
|
||||
NSDictionary* expectedHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(expectedResponse));
|
||||
NSDictionary* actualHeaders = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyAllHeaderFields(actualResponse));
|
||||
for (NSString* expectedHeader in expectedHeaders) {
|
||||
if ([expectedHeader isEqualToString:@"Date"]) {
|
||||
continue;
|
||||
}
|
||||
NSString* expectedValue = [expectedHeaders objectForKey:expectedHeader];
|
||||
NSString* actualValue = [actualHeaders objectForKey:expectedHeader];
|
||||
if (![actualValue isEqualToString:expectedValue]) {
|
||||
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", expectedHeader, expectedValue, actualValue);
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
for (NSString* actualHeader in actualHeaders) {
|
||||
if (![expectedHeaders objectForKey:actualHeader]) {
|
||||
_LogResult(@" Header '%@' not matching:\n Expected: \"%@\"\n Actual: \"%@\"", actualHeader, nil, [actualHeaders objectForKey:actualHeader]);
|
||||
success = NO;
|
||||
}
|
||||
}
|
||||
|
||||
NSData* expectedBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(expectedResponse));
|
||||
NSData* actualBody = ARC_BRIDGE_RELEASE(CFHTTPMessageCopyBody(actualResponse));
|
||||
if (![actualBody isEqualToData:expectedBody]) {
|
||||
_LogResult(@" Bodies not matching:\n Expected: %lu bytes\n Actual: %lu bytes", (unsigned long)expectedBody.length, (unsigned long)actualBody.length);
|
||||
success = NO;
|
||||
|
||||
if (_IsTextContentType([expectedHeaders objectForKey:@"Content-Type"])) {
|
||||
NSString* expectedPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
NSString* actualPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"txt"]];
|
||||
if ([expectedBody writeToFile:expectedPath atomically:YES] && [actualBody writeToFile:actualPath atomically:YES]) {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setLaunchPath:@"/usr/bin/opendiff"];
|
||||
[task setArguments:@[expectedPath, actualPath]];
|
||||
[task launch];
|
||||
ARC_RELEASE(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(actualResponse);
|
||||
}
|
||||
CFRelease(expectedResponse);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
CFRelease(request);
|
||||
}
|
||||
_LogResult(@"");
|
||||
if (!success) {
|
||||
++result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self stop];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -25,7 +25,11 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#import <TargetConditionals.h>
|
||||
#import <netdb.h>
|
||||
#if !TARGET_OS_IPHONE
|
||||
#import <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#import "GCDWebServerPrivate.h"
|
||||
|
||||
@@ -45,6 +49,9 @@ static NSData* _CRLFData = nil;
|
||||
static NSData* _CRLFCRLFData = nil;
|
||||
static NSData* _continueData = nil;
|
||||
static NSData* _lastChunkData = nil;
|
||||
#if !TARGET_OS_IPHONE
|
||||
static int32_t _connectionCounter = 0;
|
||||
#endif
|
||||
|
||||
@interface GCDWebServerConnection () {
|
||||
@private
|
||||
@@ -62,6 +69,14 @@ static NSData* _lastChunkData = nil;
|
||||
CFHTTPMessageRef _responseMessage;
|
||||
GCDWebServerResponse* _response;
|
||||
NSInteger _statusCode;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
NSUInteger _connectionIndex;
|
||||
NSString* _requestPath;
|
||||
int _requestFD;
|
||||
NSString* _responsePath;
|
||||
int _responseFD;
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -77,6 +92,18 @@ static NSData* _lastChunkData = nil;
|
||||
LOG_DEBUG(@"Connection received %zu bytes on socket %i", size, _socket);
|
||||
_bytesRead += size;
|
||||
[self didUpdateBytesRead];
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (_requestFD > 0) {
|
||||
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
return (write(_requestFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
|
||||
});
|
||||
if (!success) {
|
||||
LOG_ERROR(@"Failed recording request data: %s (%i)", strerror(errno), errno);
|
||||
close(_requestFD);
|
||||
_requestFD = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
block(buffer);
|
||||
} else {
|
||||
if (_bytesRead > 0) {
|
||||
@@ -100,8 +127,8 @@ static NSData* _lastChunkData = nil;
|
||||
|
||||
if (buffer) {
|
||||
NSMutableData* data = [[NSMutableData alloc] initWithCapacity:dispatch_data_get_size(buffer)];
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bufferChunk, size_t size) {
|
||||
[data appendBytes:bufferChunk length:size];
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
[data appendBytes:chunkBytes length:chunkSize];
|
||||
return true;
|
||||
});
|
||||
block(data);
|
||||
@@ -119,8 +146,8 @@ static NSData* _lastChunkData = nil;
|
||||
|
||||
if (buffer) {
|
||||
NSMutableData* data = [NSMutableData dataWithCapacity:kHeadersReadBuffer];
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* bufferChunk, size_t size) {
|
||||
[data appendBytes:bufferChunk length:size];
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
[data appendBytes:chunkBytes length:chunkSize];
|
||||
return true;
|
||||
});
|
||||
NSRange range = [data rangeOfData:_CRLFCRLFData options:0 range:NSMakeRange(0, data.length)];
|
||||
@@ -158,7 +185,7 @@ static NSData* _lastChunkData = nil;
|
||||
|
||||
if (buffer) {
|
||||
if (dispatch_data_get_size(buffer) <= length) {
|
||||
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* chunkBytes, size_t chunkSize) {
|
||||
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
NSData* data = [NSData dataWithBytesNoCopy:(void*)chunkBytes length:chunkSize freeWhenDone:NO];
|
||||
NSError* error = nil;
|
||||
if (![_request performWriteData:data error:&error]) {
|
||||
@@ -245,7 +272,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
[self _readBufferWithLength:SIZE_T_MAX completionBlock:^(dispatch_data_t buffer) {
|
||||
|
||||
if (buffer) {
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t offset, const void* chunkBytes, size_t chunkSize) {
|
||||
dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
[chunkData appendBytes:chunkBytes length:chunkSize];
|
||||
return true;
|
||||
});
|
||||
@@ -263,6 +290,9 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
|
||||
- (void)_writeBuffer:(dispatch_data_t)buffer withCompletionBlock:(WriteBufferCompletionBlock)block {
|
||||
size_t size = dispatch_data_get_size(buffer);
|
||||
#if !TARGET_OS_IPHONE
|
||||
ARC_DISPATCH_RETAIN(buffer);
|
||||
#endif
|
||||
dispatch_write(_socket, buffer, kGCDWebServerGCDQueue, ^(dispatch_data_t data, int error) {
|
||||
|
||||
@autoreleasepool {
|
||||
@@ -271,12 +301,27 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
LOG_DEBUG(@"Connection sent %zu bytes on socket %i", size, _socket);
|
||||
_bytesWritten += size;
|
||||
[self didUpdateBytesWritten];
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (_responseFD > 0) {
|
||||
bool success = dispatch_data_apply(buffer, ^bool(dispatch_data_t region, size_t chunkOffset, const void* chunkBytes, size_t chunkSize) {
|
||||
return (write(_responseFD, chunkBytes, chunkSize) == (ssize_t)chunkSize);
|
||||
});
|
||||
if (!success) {
|
||||
LOG_ERROR(@"Failed recording response data: %s (%i)", strerror(errno), errno);
|
||||
close(_responseFD);
|
||||
_responseFD = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
block(YES);
|
||||
} else {
|
||||
LOG_ERROR(@"Error while writing to socket %i: %s (%i)", _socket, strerror(error), error);
|
||||
block(NO);
|
||||
}
|
||||
}
|
||||
#if !TARGET_OS_IPHONE
|
||||
ARC_DISPATCH_RELEASE(buffer);
|
||||
#endif
|
||||
|
||||
});
|
||||
}
|
||||
@@ -285,7 +330,7 @@ static inline NSUInteger _ScanHexNumber(const void* bytes, NSUInteger size) {
|
||||
#if !__has_feature(objc_arc)
|
||||
[data retain];
|
||||
#endif
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, dispatch_get_main_queue(), ^{
|
||||
dispatch_data_t buffer = dispatch_data_create(data.bytes, data.length, kGCDWebServerGCDQueue, ^{
|
||||
#if __has_feature(objc_arc)
|
||||
[data self]; // Keeps ARC from releasing data too early
|
||||
#else
|
||||
@@ -655,6 +700,11 @@ static NSString* _StringFromAddressData(NSData* data) {
|
||||
}
|
||||
ARC_RELEASE(_response);
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
ARC_RELEASE(_requestPath);
|
||||
ARC_RELEASE(_responsePath);
|
||||
#endif
|
||||
|
||||
ARC_DEALLOC(super);
|
||||
}
|
||||
|
||||
@@ -664,6 +714,21 @@ static NSString* _StringFromAddressData(NSData* data) {
|
||||
|
||||
- (void)open {
|
||||
LOG_DEBUG(@"Did open connection on socket %i", _socket);
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (_server.recordingEnabled) {
|
||||
_connectionIndex = OSAtomicIncrement32(&_connectionCounter);
|
||||
|
||||
_requestPath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
_requestFD = open([_requestPath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY);
|
||||
DCHECK(_requestFD > 0);
|
||||
|
||||
_responsePath = ARC_RETAIN([NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]]);
|
||||
_responseFD = open([_responsePath fileSystemRepresentation], O_CREAT | O_TRUNC | O_WRONLY);
|
||||
DCHECK(_responseFD > 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
[self _readRequestHeaders];
|
||||
}
|
||||
|
||||
@@ -732,6 +797,38 @@ static inline BOOL _CompareResources(NSString* responseETag, NSString* requestET
|
||||
} else {
|
||||
LOG_DEBUG(@"Did close connection on socket %i", _socket);
|
||||
}
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
if (_requestPath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_requestFD > 0) {
|
||||
close(_requestFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%@.request", (unsigned long)_connectionIndex, _virtualHEAD ? @"HEAD" : _request.method];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_requestPath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
LOG_ERROR(@"Failed saving recorded request: %@", error);
|
||||
DNOT_REACHED();
|
||||
}
|
||||
unlink([_requestPath fileSystemRepresentation]);
|
||||
}
|
||||
|
||||
if (_responsePath) {
|
||||
BOOL success = NO;
|
||||
NSError* error = nil;
|
||||
if (_responseFD > 0) {
|
||||
close(_responseFD);
|
||||
NSString* name = [NSString stringWithFormat:@"%03lu-%i.response", (unsigned long)_connectionIndex, (int)_statusCode];
|
||||
success = [[NSFileManager defaultManager] moveItemAtPath:_responsePath toPath:[[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:name] error:&error];
|
||||
}
|
||||
if (!success) {
|
||||
LOG_ERROR(@"Failed saving recorded response: %@", error);
|
||||
DNOT_REACHED();
|
||||
}
|
||||
unlink([_responsePath fileSystemRepresentation]);
|
||||
}
|
||||
#endif
|
||||
if (_request) {
|
||||
LOG_VERBOSE(@"[%@] %@ %i \"%@ %@\" (%lu | %lu)", self.localAddressString, self.remoteAddressString, (int)_statusCode, _virtualHEAD ? @"HEAD" : _request.method, _request.path, (unsigned long)_bytesRead, (unsigned long)_bytesWritten);
|
||||
} else {
|
||||
|
||||
@@ -36,8 +36,10 @@
|
||||
#define ARC_AUTORELEASE(__OBJECT__) __OBJECT__
|
||||
#define ARC_DEALLOC(__OBJECT__)
|
||||
#if (TARGET_OS_IPHONE && (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0)) || (!TARGET_OS_IPHONE && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_8))
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__)
|
||||
#else
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__) dispatch_retain(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||
#endif
|
||||
#else
|
||||
@@ -47,6 +49,7 @@
|
||||
#define ARC_RELEASE(__OBJECT__) [__OBJECT__ release]
|
||||
#define ARC_AUTORELEASE(__OBJECT__) [__OBJECT__ autorelease]
|
||||
#define ARC_DEALLOC(__OBJECT__) [__OBJECT__ dealloc]
|
||||
#define ARC_DISPATCH_RETAIN(__OBJECT__) dispatch_retain(__OBJECT__)
|
||||
#define ARC_DISPATCH_RELEASE(__OBJECT__) dispatch_release(__OBJECT__)
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user