Use NSValue to wrap a C pointer - ios

I have a C type pointer variable:
a_c_type *cTypePointer = [self getCTypeValue];
How can I convert cTypePointer to NSObject type & vise versa?
Should I use NSValue? What is the proper way to do so with NSValue?

You can indeed use a NSValue.
a_c_type *cTypePointer = [self getCTypeValue];
NSValue * storableunit = [NSValue valueWithBytes:cTypePointer objCType:#encode(a_c_type)];
note that the 1st parameter is a pointer (void*). the object will contain the pointed value.
to get back to C:
a_c_type element;
[value getValue:&element];
Note that you would get the actual value, not the pointer. But then, you can just
a_c_type *cTypePointer = &element
Test it :
- (void) testCVal
{
double save = 5.2;
NSValue * storageObjC = [NSValue valueWithBytes:&save objCType:#encode(double)];
double restore;
[storageObjC getValue:&restore];
XCTAssert(restore == save, #"restore should be equal to the saved value");
}
test with ptr :
typedef struct
{
NSInteger day;
NSInteger month;
NSInteger year;
} CDate;
- (void) testCVal
{
CDate save = (CDate){8, 10, 2016};
CDate* savePtr = &save;
NSValue * storageObjC = [NSValue valueWithBytes:savePtr objCType:#encode(CDate)];
CDate restore;
[storageObjC getValue:&restore];
CDate* restorePtr = &restore;
XCTAssert(restorePtr->day == savePtr->day && restorePtr->month == savePtr->month && restorePtr->year == savePtr->year, #"restore should be equal to the saved value");
}

You simply use the method valueWithPointer: to wrap a pointer value as an NSValue object, and pointerValue to extract the pointer value.
These are just like valueWithInt:/intValue et al - they wrap the primitive value. You are not wrapping what the pointer points at. Therefore it is important that you ensure that when extract the pointer that whatever it pointed at is still around, or else the pointer value will be invalid.
Finally you must cast the extract pointer value, which is returned as a void *, back to be its original type, e.g. a_c_type * in your example.
(If you want to wrap what is being pointed at consider NSData.)
HTH

Related

Objective c set value to a property if not null [duplicate]

I have this code
if ([args valueForKey:#"showSetupScreen"]) {
BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"];
NSLog(showSetupScreen ? #"YES" : #"NO");
// meetingConfig.showSetupScreen = showSetupScreen;
}
Where args is NSMutableDictionary.
args value in my dictionary is NO but when I set to BOOL showSetupScreen = [args valueForKey:#"showSetupScreen"]; it changes into YES
Can someone help me in comprehending why this could be happening.
Attached Screenshot for your reference
A NSDictionary (or NSMutableDictionary) cannot directly contain a primitive C type, such as BOOL. Primitive numeric types (including Boolean) in NSDictionary are wrapped in NSNumber objects. See Numbers Are Represented by Instances of the NSNumber Class and Most Collections Are Objects.
Thus, use NSNumber method boolValue to extract the Boolean from the NSNumber, e.g.,
BOOL showSetupScreen = [[args valueForKey:#"showSetupScreen"] boolValue];
Or, more simply:
BOOL showSetupScreen = [args[#"showSetupScreen"] boolValue];
E.g., examples with primitive C types, including BOOL, NSInteger, and double:
NSDictionary *args = #{
#"foo": #NO,
#"bar": #YES,
#"baz": #42,
#"qux": #3.14
};
BOOL foo = [args[#"foo"] boolValue]; // NO/false
BOOL bar = [args[#"bar"] boolValue]; // YES/true
NSInteger baz = [args[#"baz"] integerValue]; // 42
double qux = [args[#"qux"] doubleValue]; // 3.14
For what it's worth, if you expand the values contained within args, that will show you the internal types for those values, and you will see that that the value associated with showSetupScreen (or foo in my example), is not a BOOL, but rather a pointer to a __NSCFBoolean/NSNumber:
[args valueForKey:#"showSetupScreen"] statement returns pointer (address in memory) and it has two options: some address (non zero value) and NULL (zero). For C programming language true is any non zero value (any address in memory in our case). And for this reason you get true in if operator and in showSetupScreen variable. But it only tells you that there is some object in the dictionary for the specified key, but not the value of this key (the value wrapped in this object). To get this value (BOOL in our case), you must call the boolValue.

Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'

Hi I am learner in Objective-c Having a warning of Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'
- (void)stopAnimating {
pause = YES;
if (timerArray) {
for (NSInteger i = 0; i < [timerArray count]; i++) {
dispatch_source_t _timer = [[timerArray objectAtIndex:i] source];
dispatch_source_cancel(_timer);
_timer = nil;
}
timerArray = nil;
}
[self removeAllFlakesWithAnimation:YES];
}
in dispatch_source_t _timer = [[timerArray objectAtIndex:i] source]; this line, how to solve, timeArray is a NSMutableArray NSMutableArray *timerArray;
We can't tell you what is wrong with your code, there is not enough information for that, but we can tell you what the compiler is doing and why it produces the error it does – then you'll have to resolve it from there.
In your line:
dispatch_source_t _timer = [[timerArray objectAtIndex:i] source];
The LHS declares are variable, _timer, of type dispatch_source_t so the RHS needs to return a value of this type. Let's look at the RHS:
[timerArray objectAtIndex:i]
which BTW you can write more succinctly as:
timerArray[i]
this indexes into an array which you have declared as:
NSMutableArray *timerArray;
the elements of an array like this have type id – which means a reference to any object. The actual type of the objects in the array in this case will not be known until runtime. The next part of the RHS is:
[<a reference so some object> source]
Objective-C allows this and will perform a check at runtime to determine that the reference object does indeed have a method source. However at compile time the compiler can look up the definition of methods called source, it does, and finds that the method returns an NSString *.
So the RHS returns an NSString * and the LHS requires an dispatch_source_t and therefore the compiler reports:
Incompatible pointer types initializing 'dispatch_source_t' (aka 'NSObject<OS_dispatch_source> *') with an expression of type 'NSString *'
Now you have to figure out whether you intended to call source or some other method which does return a value of the right type, etc. HTH
As another BTW to someone learning Objective-C: You are using a for loop to produce an index value for an array, and you only use that value to index the array once. A better way to do this is to use a for/in loop:
for (<YourObjectType> element in timerArray) {
dispatch_source_cancel([element source]);
}
You need to replace <YourObjectType> with the type of object references you've stored in timerArray, and as above the source method needs to return a dispatch_source_t value.
Objective-C has a for ... but there are other really nice ways in which you can iterate through elements of an array. I give one example for array a, in this case of NSString *
[a enumerateObjectsUsingBlock: ^ ( NSString * i, NSUInteger idx, BOOL * stop ) {
// do something with NSString * i
// its index into the array is idx if you need it
// to exit out of the loop do
* stop = YES;
}];

BOOL property from a calculation returns NSNumber with incorect value using valueForKey:

I have a simple object which has one NSNumber which is used to store some flags.
I have a conienience getter method which in fact does:
[self.flags integerValue] & SomeConstantFlag
for a property#property (readonly, nonatomic, assign) BOOL someConstantFlag
and this works fine when accesing the underlying bool value like
model.someConstantFlag
but when I try to
id value = [model valueForKey:#"someConstantFlag"];
Then it returns a bad boolean representation e.g. NSNumber with value 2, 4 etc.
Why is this happening when the declaration of the property is BOOL? Is there a "Pretty" way to overcome this issue?
Wrapping on the other hand works ok:
BOOL someBool = 42;
NSNumber* numberVal = #(someBool);
//The underlying is an __NSCFBoolean with the proper 0/1 val!
valueForKey always returns an Objective-C object, even if the property has scalar type.
From the documentation (emphasis mine):
The default implementations of valueForKey: and setValue:forKey:
provide support for automatic object wrapping of the non-object data
types, both scalars and structs.
Once valueForKey: has determined the specific accessor method or
instance variable that is used to supply the value for the specified
key, it examines the return type or the data type. If the value to be
returned is not an object, an NSNumber or NSValue object is created
for that value and returned in its place.
The return value of your method is BOOL, which is defined as
typedef signed char BOOL;
on OS X and on the 32-bit iOS platform. So what valueForKey returns is a NSNumber
containing the result of
signed char val = [self.flags integerValue] & SomeConstantFlag;
and that can be in the range -128 .. 127.
To ensure that you get only YES or NO (aka 1 or 0) write your custom getter as:
-(BOOL)someConstantFlag
{
return ([self.flags integerValue] & SomeConstantFlag) != 0;
}
Remark: On the 64-bit iOS platform (but not on 64-bit OS X), BOOL is defined as the C99 _Bool, which is a "proper" boolean type and can take only the value 0 or 1.
NSNumber *value = #([model objectForKey:#"someConstantFlag"]);
BOOL boolVal = [value boolValue];
I think you should consider the following problems. Firstly, integerValue returns NSInteger which means if you support 64Bit architecture it will return int_64 not int_32, what is more in your code here
[self.flags integerValue] & SomeConstantFlag
this does the following if flags is 00010 and somConstantFlags is 00001 the & of those will do something you probably does not expect because you will get value of 00000 which equals 0 or if they are 00011 and 00110 you will get 00010 which equals 2. So that is why when you call valueForKey you get 2 or 4 or something else depending on your flags :)
What is more in objective-C everything different then 0 is YES.
Try reconsidering your bit logic :). See The following example
enum
{
kWhite = 0,
kBlue = 1 << 0,
kRed = 1 << 1,
kYellow = 1 << 2,
kBrown = 1 << 3,
};
typedef char ColorType;
and in your setter check the following
ColorType pinkColor = kWhite | kRed;
if (pinkColor & (kWhite | kBlue | kRed | kYellow)) {
// any of the flags has been set
}
The flags kWhite, kBlue, kRed and kYellow have been set.
However, kBrown has not been set.

Storing a C float array in an NSDictionary

I am trying to store a c-float array in an NSDictionary to use later.
I was initially using an NSArray to store the C-data but NSArray is to slow for my intentions.
I am using the following code to wrap the arrays in the NSDictionary:
[self.m_morphPositions setObject:[NSValue valueWithBytes:&positionBuff objCType:#encode(float[(self.m_countVertices * 3)])] forKey:fourCC];
And retrieving the C-Float array using:
float posMorphs[(self.m_countVertices*3)];
NSValue *posValues = [self.m_morphPositions objectForKey:name];
[posValues getValue:&posMorphs];
When I retireve the array, the values are all set to 0.0 for each index which is wrong.
How can I fix this?
I also think that NSData is probably the best solution here. But just if anybody is interested: You cannot use #encode with a variable sized array, because it is a compiler directive. Therefore
#encode(float[(self.m_countVertices * 3)])
cannot work. Actually the compiler creates the encoding for a float array of size zero here, which explains why you get nothing back when reading the NSValue.
But you can create the type encoding string at runtime. For a float array,
the encoding is [<count>^f] (see Type Encodings), so the following would work:
const char *enc = [[NSString stringWithFormat:#"[%d^f]", (self.m_countVertices * 3)] UTF8String];
NSValue *val = [NSValue valueWithBytes:positionBuff objCType:enc];
NSValue is probably intented for scalar values, not arrays for them. In this case, using NSData should be much easier
NSData* data = [NSData dataWithBytes:&positionBuff length:(self.m_countVertices * 3 * sizeof(float))];
[data getBytes:posMorphs length:(self.m_countVertices * 3 * sizeof(float))];
Another solution is to allocate the array on the heap and use NSValue to store the pointer.
You can feed the bytes into an NSDictionary if they're wrapped in an NSData.
If you want to skip that, you would use an NSMapTable and NSPointerFunctionsOpaqueMemory (or MallocMemory) for the value pointer functions.
I'm not sure if it is the way you are encoding your value, but it might help to encapsulate your array into a struct.
// Put this typedef in a header
typedef struct
{
float values[3];
} PosValues;
In your code:
// store to NSValue
PosValues p1 = { { 1.0, 2.0, 3.0 } };
NSValue *val = [NSValue valueWithBytes:&p1 objCType:#encode(PosValues)];
// retrieve from NSValue
PosValues p2;
[val getValue:&p2];
NSLog(#"%f, %f, %f", p2.values[0], p2.values[1]. p2.values[2]);
The benefit to this approach is that your array is kept as an array type. Also, these structures are assignable even though raw arrays are not:
PosValues p1 = { { 1.0, 2.0, 3.0 } };
PosValues p2;
p2 = p1;

How to create an NSValue from the addition of a pointer and an integer?

I need to create a key for an NSMutableDictionary. The ideal key would be the merger of a pointer and an integer.
This will work for just the pointer
NSValue *key = [NSValue valueWithPointer:somePointer];
How do I add together somePointer + someInt and then form a key from the result?
I would do this:
NSObject *p = <object>
int n = 0xdeadbeef;
NSValue *v = [NSValue valueWithPointer:(void *)((uint32_t )p + n)];
NSLog(#"p = %p n = %x v = %#",p,n,v);
Pointers are integer types whose size depends on your platform (32 or 64 bits typically) which can be added to other integral types using a suitable cast. Just make sure to cast with a type whose length is greater than or equal to the length of a void pointer. This will ensure that you don't truncate the pointer.

Resources