Converting my project to ARC but need help with these two bits of code to do with uploading and downloading files:
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
[delegate performSelector:progressSelector withObject:(id)(100*totalBytesWritten /totalBytesExpectedToWrite)];
and then the following code, specifically the last line:
- (void)connection:(NSURLConnection *)con // IN
didReceiveData:(NSData *)data // IN
{
NSLog(#"%s: self:0x%p\n", __func__, self);
NSInteger dataLength;
const uint8_t * dataBytes;
NSInteger bytesWritten;
NSInteger bytesWrittenSoFar;
dataLength = data.length;
dataBytes = (const uint8_t * )data.bytes;
bytesWrittenSoFar = 0;
if(fileStream!=NULL)
{
do {
NSLog(#"%d",(int)bytesWrittenSoFar);
bytesWritten = [fileStream write:&dataBytes[bytesWrittenSoFar] maxLength:dataLength - bytesWrittenSoFar];
assert(bytesWritten != 0);
if (bytesWritten == -1)
{
[self downloadSucceeded:NO];
break;
}
else
{
bytesWrittenSoFar += bytesWritten;
}
}
while (bytesWrittenSoFar != dataLength);
}
dataSize+=data.length;
if(dataSize==downloadSize)
{
downloadDidSucceed=TRUE;
}
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
}
Any help is appreciated
Your problem is here
[delegate performSelector:progressSelector withObject:(id)(long)(100*dataSize/downloadSize)];
try this
[delegate performSelector:progressSelector withObject:[NSnumber numberWithLong:(100*dataSize/downloadSize)]];
In Objective-C there is distinction between scalar values like int/long/float/double and object types.
the id type is an anonymous object type. You don't know what it is, but you know that it is an object.
A long is not, and cannot be, an object. It is a simple scalar value. that's why the compiler isn't letting you cast your long to an ID type.
Sh_Khan's answer of wrapping your long value in an NSNumber solves the problem by creating an object that contains your value. You could also use an NSValue to get the same effect, but NSValue can represent things like structs that you can't represent with NSNumbers.
Related
I have the following category method on NSData. I'm trying to extract the bit field at the given index and have it return as an NSNumber. I have it working perfectly for all positive but I need it to work with negative numbers as well.
My Implementation looks as follows:
#import <Foundation/Foundation.h>
#interface NSData (ExternalDevices)
#end
#implementation NSData (ExternalDevices)
- (NSNumber *)extractLittleEndianBitFieldAtIndex:(int)index forLength:(int)length
{
//This function has limitations on the "length" parameter that are not yet know/defined
//These limitations are due to the max size of "NSInteger intData" defined below
int first_byte = index/8; //Index of the first byte containing this bit field
int last_byte = (length+index-1)/8; //Index of the last byte containing this bit field
int byte_length = last_byte - first_byte + 1; //number of bytes containing this bit field
Byte *byteArray = (Byte*)malloc(byte_length);
memcpy(byteArray, [[self subdataWithRange:NSMakeRange(first_byte, byte_length)] bytes], byte_length);
NSInteger intData = *((NSInteger *)byteArray);
free(byteArray);
return [NSNumber numberWithInt:intData];
}
+ (NSData *)dataFromHexString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
NSUInteger length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
#end
#interface Testing:NSObject
#end
#implementation Testing
- (instancetype)init
{
self = [super init];
if (self)
{
{
NSData *data = [NSData dataFromHexString:#"e30b"];
NSLog(#"%# should be 3043", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"46e0"];
NSLog(#"%# should be -8122", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"f208"];
NSLog(#"%# should be 2290", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"10e6"];
NSLog(#"%# should be -6640", [data extractLittleEndianBitFieldAtIndex:0 forLength:16]);
}
{
NSData *data = [NSData dataFromHexString:#"018900"];
NSLog(#"%# should be 137", [data extractLittleEndianBitFieldAtIndex:8 forLength:16]);
}
}
return self;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[[Testing alloc] init];
}
}
The following website seems to always yield the results I want under INT16 - Little Endian (BA)
http://www.scadacore.com/field-applications/miscellaneous/online-hex-converter.html
Although it is important to note that not every number I work with will be an INT16
Your line:
NSInteger intData = *((NSInteger *)byteArray);
is your key problem for two reasons:
byteArray may be shorter (or less likely, longer) than an NSInteger and you'll end up reading garbage. E.g. if byteArray is 2 bytes as in your examples and NSInteger is 4 bytes - which it will be in 64-bit - you'll read two bytes of garbage.
If you are converting signed values you need to sign-extend the value - that is replicate the sign bit into the higher unused bits. E.g. if you are converting a signed 16-bit field into a 32-bit signed value then the upper 16-bits need to be a replication of the most significant bit of the 16-bit value, so 0x7000 -> 0x00007000 and 0x8000 -> 0xFFFF8000.
You need to come up with an algorithm that handles these issues. You may find it easier to do the conversion a byte at a time using masking (and'ing), or'ing and shifting.
HTH
How to check NSData is string or image data , i have receive server can get 2 type of data is string and UIImage data. How to check data is string or image data. Thank in advanced.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventOpenCompleted: {
NSLog(#"NSStreamEventOpenCompleted");
} break;
case NSStreamEventHasBytesAvailable: {
if(!self.data) {
self.data = [[NSMutableData alloc] init];
}
while (self.networkStream.hasBytesAvailable) {
uint8_t buf[1024];
NSInteger len = 0;
len = [self.networkStream read:buf maxLength:1024];
if(len>0) {
[self.data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
self.bytesRead += len;
}else{
}
}
// Event when read input stream done
if (!self.networkStream.hasBytesAvailable) {
[self _didReceiveDataString:self.data];
}
}
}
Agreed with rmaddy. But check image before string, because image data may return a valid string.
if ([UIImage imageWithData:data])
{
// get image
}else if ([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding])
{
// get string
}
I would question a setup that could send string or image with no metadata to tell you which.
But if you always receive the same type of image you can examine the first few bytes to see if it contains the expected "signature". And most image formats will contain some "illegal" Unicode characters in or immediately following the "signature", so you can detect for sure that it's not a string.
It's kind of clumsy to attempt to convert the data & detect failure in order to determine its type.
One option would be to try to create an NSString from the data (NSString initWithData:encoding:). If that fails, then try to create a UIImage (UIImage imageWithData:). If that fails, you have neither.
Another option would be to check the data length. This works if you know that the longest string you will get will never be more data than the smallest image you might get.
Try this:
UIImage *image= [UIImage imageWithData:imagedata];
if(image != nil){
// correct image
} else{
// incorrect image
}
I'm using game center to send data between two players. For some reason I keep getting a deallocated instance message. Here's the code:
- (void)sendGameMove:(uint32_t)i andj:(NSString *)j {
MessageMove message;
message.message.messageType = kMessageTypeMove;
message.i = 1;
message.j = #"Testing 1 2 3s";
NSData *data = [NSData dataWithBytes:&message length:sizeof(MessageMove)];
MessageMove * messageMove = (MessageMove *) [data bytes];
NSLog(#"Message I: %i", messageMove->i);
NSLog(#"Message J: %#", messageMove->j);
[self sendData:data];
}
(I Filled in the i and j arguments for what i'm passing). In this method the NSLog statements both log what they're supposed to after creating the NSData object but when I sent that NSData object to the method [self sendData:data]:
- (void)sendData:(NSData *)data {
MessageMove * messageMove = (MessageMove *) [data bytes];
NSLog(#"Message I: %i", messageMove->i);
NSLog(#"Message J: %#", messageMove->j);
NSError *error;
BOOL success = [[GCHelper sharedInstance].match sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
if (!success) {
[self matchEnded];
}
}
In the NSLog statement the first one works fine so I get:
"Message I: 1"
in the console but for the second log statement I get :
"*** -[ respondsToSelector:]: message sent to deallocated instance"
the code to break down the data object is the exact same in the second method as in the first. Any ideas?
I'm guessing that MessageMove is a struct like:
typedef struct {
int i;
NSString *j;
} MessageMove;
The problem is that you're sending only the contents of the struct itself. In memory and on the network, it'd look something like this:
01000000 07FCA700
-----------------
i j
When the second device receives the message and tries to read the j pointer, it crashes because there's nothing there: that pointer was only valid on the origin device. The struct didn't even contain the contents of the string at all.
To fix this, you need to actually send the string in the message. Flexible array members are one way of storing a string directly in the struct:
typedef struct {
int32_t i; // explicit integer sizes are a good idea for network protocols
int32_t j_length;
char j[]; // the flexible array member
} MessageMove;
Send:
NSString *s = #"Testing 1 2 3s";
NSData *d = [s dataUsingEncoding:NSUTF8StringEncoding];
size_t messageSize = sizeof(MessageMove) + [d length];
MessageMove *mm = calloc(1, messageSize);
mm->i = 1;
mm->j_length = [d length];
memcpy(mm->j, [d bytes], [d length]);
[self sendData:[NSData dataWithBytes:mm length:messageSize]];
Receive:
MessageMove *mm = (MessageMove *)[data bytes];
assert(mm->j_length == [data length] - offsetof(MessageMove, j));
NSString *j = [[NSString alloc] initWithBytes:mm->j length:mm->j_length encoding:NSUTF8StringEncoding];
The assertion prevents reading past the end of the data buffer. offsetof is a macro from <stddef.h>.
For anything more complicated than this, I'd recommend serializing to and sending plist or JSON instead. They handle all the string copying details for you and let you send arrays and dictionary too.
In my iPhone app I am getting the device token from Apple which I am assigning a public property inside the Delegate file as shown below:
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
self.dToken = [[NSString alloc] initWithData:deviceToken encoding:NSUTF8StringEncoding];
}
The dToken property is declared as shown below:
NSString *dToken;
#property (nonatomic,retain) NSString *dToken;
But when I try to retrieve the device token from another file I get the null value.
+(NSString *) getDeviceToken
{
NSString *deviceToken = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] dToken];
NSLog(#" getDeviceToken = %#",deviceToken); // This prints NULL
return deviceToken;
}
What am I doing wrong?
I suggest you to convert token to string in this way:
self.dToken = [[[deviceToken description]
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]]
stringByReplacingOccurrencesOfString:#" "
withString:#""];
UPDATED:
As many people mentioned it is better to use next approach to convert NSData * to NSString *:
#implementation NSData (Conversion)
- (NSString *)hexadecimalString
{
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
if (!dataBuffer) {
return [NSString string];
}
NSUInteger dataLength = [self length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
[hexString appendFormat:#"%02lx", (unsigned long)dataBuffer[i]];
}
return hexString;
}
#end
From the discussion at Best way to serialize an NSData into a hexadeximal string, here is a better way to do it. Is longer, but your code will be future-proof if Apple changes the way NSData emit debugger descriptions.
Extend NSData as follows:
#implementation NSData (Hex)
- (NSString*)hexString {
unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (self.length*2));
unsigned char* bytes = (unsigned char*)self.bytes;
for (NSUInteger i = 0; i < self.length; i++) {
unichar c = bytes[i] / 16;
if (c < 10) c += '0';
else c += 'A' - 10;
hexChars[i*2] = c;
c = bytes[i] % 16;
if (c < 10) c += '0';
else c += 'A' - 10;
hexChars[i*2+1] = c;
}
NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars
length:self.length*2
freeWhenDone:YES];
return [retVal autorelease];
}
#end
I know that this is an old question and that this may be new information that has come up since then, but I'd just like to point something out to all of the people who are claiming that using the description method is a really bad idea. In most cases, you'd be exactly right. The description property is generally just used for debugging, but for the NSData class, it's specifically defined as returning a hexadecimal representation of the receivers contents which is exactly what is needed here. Since Apple has put it in their documentation, I think you're pretty safe as far as them changing it.
This can be found in the NSData Class Reference here: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/Reference/Reference.html
I have a variable which is a CFStringRef and I perform a check to make sure its not 1 specific value. If it is then I want to set it to the NSString equivalent of #""
Here's the code
CFStringRef data = = CFDictionaryGetValue(dict, kABPersonAddressStateKey);
NSComparisonResult result = [(NSString *)data compare:element options:compareOptions];
if(NSOrderedAscending == result) {
// Do something here...
}
else if (NSOrderedSame == result) {
// Do another thing here if they match...
data = "";
}
else {
// Try something else...
}
So in the if else block, I want to set it to "" but Xcode warns me that it is an invalid pointer type.
CFStringRef is immutable object so you must create new instance with different value:
data = CFStringCreateWithCString (NULL, "", kCFStringEncodingUTF8);
And remember that you need to release that value.
But since CFStringRef and NSStrings types are toll-free bridged you can just use NSString in your code (that will probably makes it easier to understand and support it later):
NSString *data = (NSString*)CFDictionaryGetValue(dict, kABPersonAddressStateKey);
NSComparisonResult result = [(NSString *)data compare:element options:compareOptions];
if(NSOrderedAscending == result) {
// Do something here...
}
else if (NSOrderedSame == result) {
// Do another thing here if they match...
data = #"";
}
else {
// Try something else...
}