Caching the struct Object - objective-c

How do I create a cache for a struct pointer object in Objective-C? Is there any third party component for caching objects as Java and .NET have?
I have the following struct:
typedef struct _news {
references
char *headline;
char *story_url;
} news;
I have a double pointer for the above struct in an interface class. I would like to cache it for some time using Objective-C.

Are you searching for something like this?
// save
NSMutableData *data = [[NSMutableData alloc] init];
[data appendBytes:&p length:sizeof(news)];
// read
struct news *begin = (struct news *)[data bytes];

Related

NSMutableData increment the underneath pointer and pass it to other APIs

I have been given a task to replace all uint_8 * to NSMutableData.
In the existing code, a lot of pointer arithmetic is done on uint8_t and then passed onto other APIs which also accept uint8_t *.
However, when replacing uint_8 * with NSMutableData, how do I pointer arithmetic on NSMutableData?
One approach is
convert NSMutableData to uint8_t *.
do pointer arithmetic on uint8_t *.
convert uint8_t * back to NSMutableData and pass it to other APIs.
however, when converting uint8_t * to NSMutableData, I do not have length.
How to solve this?
Old code
func(uint8_t *p);
func2() {
uint8_t *p; //points to some piece of memory
func(p+10); //pass base address+ 10 bytes
}
New Code
func(NSMutableData *p);
func2() {
NSMutableData *data = [[NSMutableData alloc]
initWithLength:size];
// now, how to pass data + 10 bytes to func
func(???)
}

Returning variable size byte array and free the memory

I am writing a proxy .c file for using OpenSSL library for RSA encryption. Usable for iPhone. Since encrypted data sized is not known to the calling class in Objective C, the container is not initialized and I want to keep it dynamic. The char array and the received size is called by reference. Its memory is dynamically allocated in ssl_encrypt_public_rsa and the caller has to free it. I don't like the idea to give the responsibility to the caller.
Is there any other robust method that you can suggest?
openssl implementation in .c (later compiled to static lib .a file)
// calling function must free cipherText memory
void ssl_encrypt_public_rsa(RSA *rsaKey, int plainLength, unsigned char *plainText,
int *cipherLength, unsigned char **cipherText)
{
int enc_len = RSA_size(rsaKey);
unsigned char *enc_bytes = malloc(enc_len * sizeof(char));
int encSize = RSA_public_encrypt(plainLength, plainText, enc_bytes, rsaKey, RSA_PKCS1_PADDING);
*cipherText = enc_bytes;
*cipherLength = encSize;
}
void ssl_encrypt_mips(int plainLength, unsigned char *plainText,
int *cipherLength, unsigned char **cipherText)
{
// rsaKeyMips is defined and initialized earlier
ssl_encrypt_public_rsa(rsaKeyMips, plainLength, plainText, cipherLength, cipherText);
}
Calling function Objective C .m file
-(NSData *) encryptMips:(NSData *)inData
{
int encSize = 0;
unsigned char *cipherBytes;
ssl_encrypt_mips((int)inData.length, (unsigned char *)inData.bytes, &encSize, &cipherBytes);
if (encSize == -1 ) return nil;
NSData * d = [NSData dataWithBytes:cipherBytes length:encSize];
free(cipherBytes);
return d;
}
It is unclear what you are asking, as you've already wrapped the C call in a wrapper which takes care of the free(). If you wish to remove the free() from your wrapper, and serendipitously avoid a copy, you can change your:
NSData *d = [NSData dataWithBytes:cipherBytes length:encSize];
free(cipherBytes);
to:
NSData *d = [NSData dataWithBytesNoCopy:cipherBytes length:encSize free:YES];
which hands ownership of the malloc() block to the NSData instance, which will free() it later when no longer needed.
HTH

How to send an array of skspritenodes to another player in objective c?

In my game, one player makes an array of skspritenodes in a random order that I need to send to all other players in the match. I am not sure if this is possible or if I need a workaround that does the same thing, but in examples, I have seen a typedef struct containing pointers like the one below
typedef struct {
Message message;
NSString * piece;
SKSpriteNode * pieces;
} MessagePieceList;
The code above gives me the error "ARC forbids Objective-C objects in structs" when I try creating the NSString or SKSpriteNode. I have also tried
__unsafe_unretained NSString * piece;
but then I believe I get an empty value when it reaches the other players in my didReceiveData since it is not retained. I've been able to send an integer no problem by doing the following
GameScene.m has:
_networkingEngine = [[MultiplayerNetworking alloc] init];
int piece = 5;
[_networkingEngine sendpiecesOrder:piece];
MultiplayerNetworking.h has:
-(void)sendpiecesOrder:(int)piece1;
MultiplayerNetworking.m has:
typedef struct {
Message message;
int piece;
} MessageList;
-(void)sendpiecesOrder:(int)piece {
MessageList messagePiece;
messagePiece.message.messageType = kMessagePieceOrder;
messagePiece.piece = piece;
NSData *data = [NSData dataWithBytes:&messagePiece length:sizeof(MessageList)];
[self sendData:data];
} else if(message->messageType == kMessagePieceOrder) {
MessageList *messagePieceList1 = ( MessageList *)[data bytes];
NSLog(#"Pieces order message received");
NSLog(#"%d", messagePieceList1->piece);
This prints 5 when the other player(s) receives the data in their didReceiveData method. If I cant directly send the array of SKSpriteNodes, is there a way to mold this approach so that it somehow communicates the list of SKSpriteNodes to another player?

__unsafe_unretained in struct

Let say I have a struct in which I declare like so:
struct myStruct
{
NSString *aString;
}
The above gives error.
I can, however, fix the error by:
struct myStruct
{
__unsafe_unretained NSString *aString;
}
It silences the error, but will crash at runtime, because I suppose aString is immediately released.
I have tried __strong instead but it won't compile.
Is there any other way I can store an object within the struct and use it properly?
You can create a new object and use this as a pointer to a struct (as this is what a Objective C object is). So if you create a subclass of NSObject with instance variables that you require you can treat it exactly like a pointer to a structure (once you have initialised it). i.e.
myObj = [[myObjClass alloc] init];
myObj->instanceVariable1 = #"myString";
As mentioned in the comments below you need to declare the variables in the interface like this:
#interface myObjStruct : NSObject
{
#public
NSString *instanceVariable1;
}
With an NSString you can use a CFStringRef instead, or cast your NSString * to a CFString and retain it with a CFRetain(), or use CFBridgingRetain to get the incremented retain count immediately. You can do this with any type that is toll free bridged from a CF type (such as CFArray CFDictionary).
struct testStruct {
CFStringRef str;
};
- (void)aMethod
{
NSString *string = #"Hello struct";
struct testStruct test = {
CFBridgingRetain(string),
};
}
You now have ownership of the string, and will need to call CFRelease on the test.str at some point to not leak memory. To get a NSString back you cast it like this NSString *string = (__bridge NSString *)test.str;.
The above code has incremented the retain count of the string object. It's possible to get this to work for any object like this:
struct testStruct {
__unsafe_unretained AnyObj *obj;
};
- (void)aMethod
AnyObj *aObj = [[AnyObj alloc] init];
CFBridgingRetain(aObj); \\increment the retain count.
struct testStruct test = {
aObj,
};
aObj = nil;
NSLog(#"%#", aObj);
}
To release this object later you would need to do CFRelease((__bridge CFTypeRef)(test.obj));. Note that if you remove the CFBridgingRetain(aObj); this code will probably crash.
You could also try having a play with id objc_retain(id value); Although to use this you will need to manually include the arc.h header see How to import objc_retainAutoreleasedReturnValue? you would use this to increment the retain value much like the code above but without the need for casting. You'd also have to use the equivalent release function.
Toll-Free Bridged Types
__bridge transfers a pointer between Objective-C and Core Foundation with no transfer of ownership.
__bridge_retained or CFBridgingRetain casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to you. You are
responsible for calling CFRelease or a related function to relinquish
ownership of the object.
__bridge_transfer or CFBridgingRelease moves a non-Objective-C pointer to Objective-C and also transfers ownership to ARC. ARC is responsible
for relinquishing ownership of the object.
Example (not recommend)
It seems __bridge_retained, __bridge_transfer don't allow ownership transfer to/from same type. So I used additional __bridge for type casting.
I've test confirmed NSString objects are released without leaks.
#define TEST_COUNT 10000
struct myStruct
{
NSString* __unsafe_unretained aString;
};
static struct myStruct* myArray = NULL;
static void allocString()
{
myArray = (struct myStruct*) malloc(sizeof(struct myStruct) * TEST_COUNT);
for (int i=0; i<TEST_COUNT; ++i)
{
NSString* v = [[NSString alloc] initWithFormat:#"%d", i];
myArray[i].aString = (__bridge NSString*)(__bridge_retained void*) v;
}
}
static void freeString()
{
if (myArray)
{
for (int i=0; i<TEST_COUNT; ++i)
{
if (myArray[i].aString)
{
NSString* v = (__bridge_transfer NSString*) (__bridge void*) myArray[i].aString;
v = nil;
}
}
free(myArray);
}
}

NSKeyedArchiver: How to archive an NSValue containing a custom struct

Is there any way I can use an NSKeyedArchiver to encode an NSValue that contains a custom struct?. I have an NSDictionary which contains structs wrapped in NSValues and I want to archive this as part of a larger object graph but I receive the following error:
-[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs
However, consider the following:
//Copy of the CGPoint declaration
struct MYPoint {
CGFloat x;
CGFloat y;
};
typedef struct MYPoint MYPoint;
CGPoint point = {1.0, 1.0};
MYPoint myPoint = {1.0, 1.0};
NSLog(#"CGPoint: %s", #encode(CGPoint)); //CGPoint: {CGPoint=ff}
NSLog(#"MYPoint: %s", #encode(MYPoint)); //MYPoint: {MYPoint=ff}
NSValue *CGPointValue = [NSValue valueWithBytes:&point objCType:#encode(CGPoint)];
NSData *CGPointData = [NSKeyedArchiver archivedDataWithRootObject:CGPointValue];
//NO ERROR
NSValue *MYPointValue = [NSValue valueWithBytes:&myPoint objCType:#encode(MYPoint)];
NSData *MYPointData = [NSKeyedArchiver archivedDataWithRootObject:MYPointValue];
//ERROR: -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs
Is it just the case that "this archiver cannot encode your structs" and thats the end of the story or is it possible to get the same behaviour as CGPoint but for custom structs?
I will probably just create a small custom object that wraps an NSValue and implements NSCoding to work around it, but I am curious about the contradiction shown in the code above with CGPoint and wonder if there is a way to extend NSValue to get the same behaviour.
There are issues with something to do with #encode and anonymous structs. Try changing the struct definition to:
typedef struct MYPoint {
CGFloat x;
CGFloat y;
} MYPoint;
If that doesn't work, you can wrap the struct using NSData:
[NSData dataWithBytes:&myPoint length:sizeof(MYPoint)];
The following example illustrates encoding a custom C structure.
//assume ImaginaryNumber defined:
typedef struct {
float real;
float imaginary;
} ImaginaryNumber;
ImaginaryNumber miNumber;
miNumber.real = 1.1;
miNumber.imaginary = 1.41;
NSValue *miValue = [NSValue valueWithBytes: &miNumber withObjCType:#encode(ImaginaryNumber)];
ImaginaryNumber
miNumber2;
[miValue getValue:&miNumber2
];
The type you specify must be of constant length. You cannot store C strings, variable-length arrays and structures, and other data types of indeterminate length in an NSValue—you should use NSString or NSData objects for these types. You can store a pointer to variable-length item in an NSValue object. The following code excerpt incorrectly attempts to place a C string directly into an NSValue object:
/* INCORRECT! */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:myCString withObjCType:#encode(char *)];
In this code excerpt the contents of myCString are interpreted as a pointer to a char, so the first four bytes contained in the string are treated as a pointer (the actual number of bytes used may vary with the hardware architecture). That is, the sequence “This” is interpreted as a pointer value, which is unlikely to be a legal address. The correct way to store such a data item is to use an NSString object (if you need to contain the characters in an object), or to pass the address of its pointer, not the pointer itself:
/* Correct. */
char *myCString = "This is a string.";
NSValue *theValue = [NSValue valueWithBytes:&myCString withObjCType:#encode(char **)];
Here the address of myCString is passed (&myCString), so the address of the first character of the string is stored in theValue.
Important: The NSValue object doesn’t copy the contents of the string, but the pointer itself. If you create an NSValue object with an allocated data item, don’t free the data’s memory while the NSValue object exists.