Casting an (NSString *) to (int *)? - objective-c

I have an NSString.
NSString *str;
And I need to store it in a struct.
struct {
int *s;
} st;
And set it.
st.s = str;
So, how should I go about retrieving it?
return (__bridge_retained NSString *)st.s;
I've tried the above, and it gives the error: Incompatible types casting 'int *' to 'NSString *' with a __bridge_retained cast.
Answered the question. Simply define the NSString in the struct like this.
struct {
__unsafe_unretained NSString *s;
} st;
Thanks, Carl Veazey!

To store an Objective-C object in an struct you have a couple of options, the one I see most is to store it in the struct as __unsafe_unretained and then maintain a strong reference to it elsewhere.
From the "Common Issues While Converting a Project" section of the ARC Transition Notes:
If using Objective-C objects is sub-optimal, (maybe you want a dense
array of these structs) then consider using a void* instead. This
requires the use of the explicit casts...
They seem to imply __bridge is the way to cast void * to id but are not 100% clear on this.
The other option, which makes more sense to me personally and I've seen more often I think:
Mark the object reference as __unsafe_unretained. ... You declare the
structure as: struct x { NSString * __unsafe_unretained S; int X; }
Hope this helps!

Related

Is it possible to call "location" on rangerOfCharacterFromSet without dot notation in objective-c?

I am trying to write this without dot notation but can't figure it out:
[[textField text] rangeOfCharacterFromSet:someInstanceVar].location
I keep getting bad receiver type NSRange (aka '_struct NSRange')
Is this not possible?
Kind regards
You cannot call that without the dot.
rangeOfCharacterFromSet returns a NSRange, which is a plain C struct:
typedef struct _NSRange {
NSUInteger location;
NSUInteger length;
} NSRange;
and not an Objective-C object. .location accesses the first member of that struct
and is pure C syntax.
That has nothing to do with the dot-notation for properties, or with method calls.

Having difficulties understanding pointers in Objective-C

So I understand that in ObjC everything lives in the Heap, and everything has a pointer to it. I'm reading through O'Reilys book and I'm grasping most things, but when I'm following through the tutorials/examples and something like this comes up
NSMutableArray *bar = [[[foo alloc] init] callMethod];
The * is right next to bar, but then you have things like
- (NSString *)createDeck:(NSString *)numOfCards;
Why is NSString * and not - (NSString)*createDeck:(NSString)*numOfCards;?
Any help understand things concept would be great thanks.
Edit:
NSUInteger *randomIndex = arc4random() % [deck count];
As Where
NSUInteger randomIndex = arc4random() % [deck count];
Works fine, how come removing the pointer in this case works?
tl;dr
The type is NSString * and that's why you have
- (NSString *)createDeck:(NSString *)numOfCards;
The return type and the argument type are enclosed within the parentheses.
Concerning the last question, an NSUInteger is not a object, despite the naming may suggest otherwise. Being a native type it lives on the stack and you don't need a pointer to it.
If you cmd-click on the type name, you'll find that it's actually a typedef for unsigned int (or unsigned long, depending on the architecture).
Discussion
Variables in C (and consequently in Objective-C) are declared using declarators, which are composed by a type and an identifier. In your example NSString * is the type and bar is the identifier.
NSString * bar;
^^^^^^^^^^ ^^^
type identifier
Declarators without an identifier are called abstract declarators, and are commonly used in C in three cases:
casting
float x = (float)2/4;
argument of sizeof()
sizeof(int *);
declaring argument types of a function
void foo(int *, float);
In Objective-C they are also used for return and argument types of methods, and that's why you have
- (NSString *)createDeck:(NSString *)numOfCards;
(Most of the information about declarators are adapted from http://nilsou.com/blog/2013/08/21/objective-c-blocks-syntax/)
Concerning the position of the asterkisk,
NSString *bar;
NSString * bar;
NSString* bar;
are all valid ways in to declare a variable of type pointer to NSString, aka NSString *.
Which one to use is a pure matter of personal taste, even though I believe the first one is the most common.

Strange warning when try to return array

Heey
When I'm trying to return a array I'm always getting this strange "waring" message but it does not interrupt my App
Returning 'ABRecordRef' (aka 'const void *') from a function with result type 'ABRecordRef ' (aka 'const void *') discards qualifiers
Here is my code where I'm getting this message
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
{
//TODO: add lastname, phonenumber etc.
// Load the contacts
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
NSArray *thePeople = (__bridge NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
for (id person in thePeople){
NSString *firstname = (__bridge NSString*) ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
if([firstname isEqualToString: fname]){
return (__bridge ABRecordRef)(person);
}
}
return NULL;
}
Can someone please explain me why I get here a Waring ..
Thanks for help and fast answer
Remove the * here:
- (ABRecordRef *) findContactsContainingName: (NSString *) fname
^
ABRecordRef is already defined as a pointer.
ABRecord is C API and it work in CoreFoundation ways.
In CoreFoundation (and AddressBook) objects are implemented as C structs, and pointers are used to reference them. A string in CoreFoundation is CFStringRef, which is interchangeable (or rather, toll-free bridged) with Foundation object, NSString *. (i.e. the "Ref" in CFStringRef implied a * in it - think it as CFString *, or rather struct __CFString *)
Similarly, ABRecordRef is ABRecord * and hence your return type, ABRecordRef * is actually ABRecord **, a secondary pointer. This is what the compiler is complaining.
You can check out the source code of GNUstep CoreBase and you will find out why. GNUstep is a open-source clone of Cocoa (it predates Cocoa!) for Linux and studying its source code can be very helpful on understanding how Cocoa work under the hood.

Objective-C accessing float getters with variable names

Let's say I have an NSArray called myArray of NSStrings (#"a0",#"a1",#"a2")
Then in a fast enumeration I loop into my array to build properties according to that NSStrings. I've got a problem accessing that properties.
I'm trying something like that :
#property (nonatomic) float a0propertyLow;
#property (nonatomic) float a0propertyHigh;
#property (nonatomic) float a1propertyLow;
#property (nonatomic) float a1propertyHigh;
..
.. etc.
for (NSString *aPos in myArray) {
NSString *low = [NSString stringWithFormat:#"%#propertyLow",aPos];
NSString *high = [NSString stringWithFormat:#"%#propertyHigh",aPos];
SEL lowSel = NSSelectorFromString(low);
SEL highSel = NSSelectorFromString(high);
if ([self respondsToSelector:lowSel]&&[self respondsToSelector:highSel]) {
id sumPartOne = [self performSelector:lowSel];
id sumPartTwo = [self performSelector:highSel];
float bla = (float)sumPartOne + (float)sumPartTwo;
}
}
I know my code is wrong but I don't know how to make it work.
My problem is that lowSel and highSel are getters which returns float but the perform selector method returns id which is ok for an object but not for floats.
So, how can I access my float getters with variable names ? I'm sure answer must be simple but it seems that my mind is looking for something complicated (and which obviously doesn't work) so I'm asking for help :)
Thank you very much for your help
You can't use performSelector: to call a method that returns a scalar value. The documentation for performSelector: clearly says what you have to do:
For methods that return anything other than an object, use NSInvocation.
An NSInvocation is a little more complex to set up but more flexible regarding arguments and return types.
In your case, it is probably easier to use Key-Value Coding instead:
[self valueForKey:low];
takes the return type into account and will automatically wrap the float in an NSNumber.
If you really need to use these getter methods, you can change your properties to double and use objc_msgSend_fpret():
#include <objc/runtime.h>
#include <objc/message.h>
double arg0 = objc_msgSend_fpret(self, lowSel);
If you can avoid getters (I know, that's not good practice, but anyway, it works for sure with the following method), and use the instance variables directly:
void *object_getIvarPtr(id obj, const char *name)
{
if (!obj || !name)
{
return NULL;
}
Ivar ivar = object_getInstanceVariable(obj, name, NULL);
if (!ivar)
{
return NULL;
}
return ((char *)obj + ivar_getOffset(ivar));
}
float arg0 = *(float *)object_getIvarPtr(self, [lowSel UTF8String]);
Hope this helps.
One way you can do is convert your floats into objects at runtime such as:-
NSString *str=[NSSTring stringWithFormat:#"%f",yourFloatValue];
and then u can retrive it using
[str floatValue];

return from incompatible pointer types

The below code generates the incompatible pointer type error:
char *PLURAL(int objects, NSString *singluar, NSString *pluralised) {
return objects ==1 ? singluar:pluralised;}
I am new to objective-C and programming in general so can some one help me with this error?
An NSString * is not the same as a char * (or "C-string" in Objective C terminology). You can't convert a pointer from one to the other implicitly like that. You'll have to use a method like cStringUsingEncoding. Also, NSString is immutable, so you'll have to return a const char *.
Alternatively, you could simply return the NSString * instead of char *.
Change the return value to NSString* and you should be fine. You are specifying a return value of char* but actually returning NSString*.
Change it to:
NSString *PLURAL(int objects, NSString *singluar, NSString *pluralised) {
return objects ==1 ? singluar:pluralised;
}
char * is not NSString !