Wrap a struct inside a property into NSValue - objective-c

I use NSValue to wrap a struct defined by me, but when the struct is accesed through a #property I get this error: Address of property expression requested.
Can this be done using the property or I shall store the struct in variable first?
Non-valid code:
NSValue *previousTileObj = [NSValuevalue:&self.previousTile withObjCType:#encode(RobotTile)];
Valid code:
RobotTile localPreviousTile = self.previousTile;
NSValue *previousTileObj = [NSValuevalue:&localPreviousTile withObjCType:#encode(RobotTile)];

You cannot take the address of the result of a call to the accessor of an object's property.
This is because the statement self.previousTitle is just syntactic sugar for the Objective-C message call [self previousTitle], which has semantics similar to that for function calls in that the resulting value has no real location in memory, and therefor it cannot have its address taken with the & operator.
You can, however, cheat the result into an addressable object by wrapping the accessor call within a C array literal. Since arrays are essentially pointers to their first element, this will result in a pointer to the desired data.
NSValue *previousTileObj = [NSValue value:(RobotTile []){self.previousTile}
withObjCType:#encode(RobotTile)];

You could take advantage of structure support in key-value coding and do:
NSValue *previousTileObj = [self valueForKey:#"previousTile"];

Related

Why is NSArray mutable when used from Swift?

I have an objective-c header with the following property
#property (nullable, nonatomic, strong) NSArray<CustomObject *> *customObjects;
If I create a swift extension of that class I can now remove objects from the NSArray:
self.customObjects?.remove(at: 0)
Also if I do
print(type(of: self.customObjects))
I get:
Array<CustomObject>
Aren't NSArrays immutable ? Does Swift create a shallow copy whenever we edit it?
Your property is (implicitly) declared readwrite in ObjC. This means you can change the property writing a new NSArray instance that replaces the old (in which case the new instance's constants might be derived by first reading the other NSArray instance that's the existing value of the property):
NSArray *currentObjects = self.customObjects;
// one of many ways to derive one immutable array from another:
NSArray *newArray = [currentObjects subarrayWithRange:NSMakeRange(1, currentObjects.count - 1)];
self.customObjects = newArray;
In Swift, your property comes across as a Swift.Array (that is, the Array type from the Swift standard library), which is a value type. Every assignment semantically creates a copy. (The expensive work of performing the copy can be deferred, using a "copy on write" pattern. Arrays of reference types, like objects, copy references instead of storage, so it's essentially a "shallow copy".)
Mutating operations do this, too:
let currentObjects1 = self.customObjects
currentObjects1.remove(0) // compile error
// currentObjects1 is a `let` constant so you can't mutate it
var currentObjects = self.customObjects
currentObjects.remove(0) // ok
print(self.customObjects.count - currentObjects.count)
// this is 1, because currentObjects is a copy of customObjects
// we mutated the former but not the latter so their count is different
self.customObjects = currentObjects
// now we've replaced the original with the mutated copy just as in the ObjC example
When you have a readwrite property in Swift, and the type of that property is a value type like Array (or is an ObjC type that's bridged to a value type, like NSArray), you can use mutating methods directly on the property. That's because calling a mutating method is semantically equivalent to reading (and copying) the existing value, mutating the copy, and then writing back the changed copy.
// all equivalent
self.customObjects.remove(0)
self.customObjects = self.customObjects.dropFirst(1)
var objects = self.customObjects; objects.remove(0); self.customObjects = objects
BTW: If you're designing the API for the ObjC class in question here, you might consider making your customObjects property nonnull — unless there's a meaningful semantic difference between an empty array and a missing array, your Swift clients will find it cumbersome needing to distinguish the two.

What does the const specifier do, before a pointer to object?

If I have a C-string like this:
const char* str= "hello";
I know well that I can't change any character of the string because the pointer is const.
But if I have this:
- (CLLocationDistance)distanceFromLocation:(const CLLocation *)location
I can still mutate the object state using it's methods.
If the pointer is to a NSMutableString, I'm still able to mutate it.
Then what does the const stand for?
In that method declaration, location is a pointer to a constant CLLocation. But when you send a message to the location object, the const-ness is not preserved; the method that handles the message sees self as a non-const object. (Note that this is different than C++, which supports const member functions where this is a pointer to a constant object.)
So the const in that declaration is not particularly useful. Perhaps it was written by someone used to the C++ way of doing things.
When you do see const attached to an object pointer in Objective-C, it is usually like this:
extern NSString * const SomeConstantString;
This declares SomeConstantString as a constant pointer to some non-constant object (in this case, an NSString). The pointer itself is constant, so your program can't change SomeConstantString to point to some other NSString object.
I know well that I can't change any character of the string because
the pointer is const.
No, the pointer is mutable. The characters it points to are const.
I can still mutate the object state using it's methods.
There is no const-correctness for Objective-C objects like there is in C++. The compiler does not care which messages (mutating or not) you send to a const object. So there's no sense in declaring a pointer to a const object. The cited framework method is an anomaly, probably an oversight.
Mind the difference:
// constant pointer
char * const str = "";
// pointer to constant (two equivalent ways)
const char * str = "";
char const * str = "";
The keyword const applies applies to whatever is immediately to its left. If there is nothing to its left, it applies to whatever is immediately to its right.
In Objective-C all method parameters are always passed by value. This includes primitives, structs, unions, and pointers, and any other made up type.
Note that you can't have variables of type object. A expression like NSObject o; produces a compiler error with message "Interface type cannot be statically allocated".
The only way to pass an object is passing a pointer. The pointer is passed as value, but lets the code inside the method reference the object and change it. So in a way, it is as if you are passing the object by reference (in reality you are passing the pointer by value).
When compiling an Objective-C program, the methods are turned into C functions, and each "message send" (aka "method call", though it isn't exactly the same) is ran using the runtime function objc_sendMsg. This function doesn't know or care if you qualified the object with const or not. If you want an immutable object, you have to code that immutability inside the object. Example:
// const qualifying an object is ignored whether in variables or method arguments:
const NSMutableArray *array = [NSMutableArray new]; // const is ignored
-(void)someMethod:(const NSMutableArray *)array { ... // const is ignored
// calling the methods that mutate the object works fine
[array removeAllObjects];

converting dot syntax to bracket syntax on a struct

I have a property of CGSize:
#property (nonatomic) CGSize descriptionSize;
'
#synthesize descriptionSize = _descriptionSize;
I can access the height through the dot syntax:
self.descriptionSize.height = 35;
but how does this work with the bracket syntax?
[self setDescriptionSize:???];
Looked stupid simple to me, but I can't get the clue. Thanks in advance!
This is one of the pitfalls of dot notation for properties: Those two dots in self.descriptionSize.height look the same but mean very different things. The first is a property accessor which maps to a "get descriptionSize" method, but the second is an old-school struct reference. The first dot returns a CGSize scalar, NOT a pointer to the size value in the object. When the second dot sets the height in that returned CGSize, it's setting a value on the stack instead of changing the value in the object. This is how you have to do it:
CGSize size = self.descriptionSize;
size.height = 35;
self.descriptionSize = size;
…or the equivalent without properties dot notation:
CGSize size = [self descriptionSize];
size.height = 35; // we still use the dot here: size is a struct not an object
[self setDescriptionSize:size];
The implementation of descriptionSize will return a copy of the CGSize struct, so you can't work directly with that and hope it will work. What you need to do is get the whole of the CGSize struct, modify it, and then pass it back in:
CGSize size = [self descriptionSize];
size.height = 35;
[self setDescriptionSize:size];
However given you are working on a property of self and the property isn't an object, which requires memory management, the most efficient way of modifying the size is:
_descriptionSize.height = 35;
However you'd use the former getter/setter approach if:
The object was not self.
You had manually written the setter method to do something as a side-effect of changing the size (for example invalidating bits of the view in order to automatically update the view).
Dot syntax can mean two different things: Either a struct reference (CGSize is a C struct), or an objective-C message send.
Theoretically, if you have a method like - (void)doSomething;, you could call it like this: myObject.doSomething; //bad style. Don't do this. Dot syntax is not meant for calling methods that actually do stuff, other than getting or setting values (although nothing in the language or the IDE is going to stop you).
Synthesizing properties creates accessor methods: - (myType)myProperty and - (void)setMyProperty:(myType)newValue. Here, dot syntax lets you access the getter in the ordinary way (because the getter is an ordinary Objective-C method), and has a special case for the setter: myObject.myProperty = newValue gets translated to [myObject setMyProperty:newValue].
This means you can switch between dot syntax and Objective-C style message sending syntax for properties (and technically for all other parameter-less Objective-C method sends), but you must use dot syntax to access struct members. Structs are not objects, and they know now methods.

Implementing a Dispatch Table in Objective-C: how to declare an array of selectors

I'm trying to implement a dispatch table, so that I can call a selector with the following example code:
NSInteger i = 2;
[myObject performSelector:selectors[i]];
I'm trying to store user preferences which affect which method of an API gets called. Right now, I use the string name of the selector and use NSSelectorFromString, but that's a bit messy. If I use a dispatch table, then I can store an enum instead.
How can I make an array of selectors, or a dispatch table in Objective-C?
Edit:
The compiler complains when I try to set an array of selectors as a property. #property SEL[] won't compile.
Edit2:
I'm using my KosherCocoa API library and I want to call a single method at once, based on a saved user setting. I'm saving to and reading from a Plist file.
You can use the SEL type to hold selectors. Simply:
SEL dispatchTable[3] = { #selector(doThis:),
#selector(doThat:),
#selector(doTheOther:)
};
To your edit, use an NSArray/NSDictionary/etc of selectors as your property instead. You are not allowed to use C arrays as properties in Objective C; they are not one of the supported types (which are ObjC objects, CF types and basic C 'Plain Old Data' types.)
OK, on our comments below, you need to wrap the selector in an NSValue to allow you to use it in an objc container (because SEL is a C pointer type):
NSMutableArray * dispatchTable2 = [[NSMutableArray alloc] initWithCapacity:3];
SEL selIn = #selector(doThis:);
// Wrap the selector in an NSValue instance
[dispatchTable2 addObject:[NSValue valueWithPointer:selIn]];
// On extracting:
NSValue * valOut = [dispatchTable2 objectAtIndex:0];
SEL selOut = [[dispatchTable2 objectAtIndex:0] pointerValue];
[anObject performSelector:selOut];
So now your table is an objc container stored as a property or ivar, and you use NSValue to wrap SEL pointers with valueWithPointer: and get the SEL out with pointerValue.
I would recommend using NSInvocation instead of selectors. They are far more flexible, as you can send the same invocation to many objects and you can change its properties as you go.
One way to do this is using an array of NSStrings, then converting those to SELs at runtime, if that increases readability for you..
NSString *selectors[] = { ... }
[myObject performSelector:NSSelectorFromString(selectors[i])];
To use this as a property, use
#property(nonatomic, assign) NSString **selectors;

Correct way of setting a BOOL property

I have a BOOL property that I want to set in my class initializer.
#property (assign, nonatomic) BOOL isEditMode;
- (id)init
{
. . .
[self setValue:NO forKey:isEditMode];
return self;
}
The compiler gives me an "Incompatible integer to pointer conversion" warning. What am i doing wrong here?
The Key-Value Coding method setValue:forKey: only accepts objects as arguments. To set a BOOL, you need to wrap the number in a value object with [NSNumber numberWithBool:NO]. But there's little reason to do that. Key-Value Coding is a roundabout way to accomplish this. Either do self.isEditMode = NO or just isEditMode = NO. The latter is preferable in an init method (because setters can run arbitrary code that might not be desirable before an object is fully set up).
But to elaborate on the first point: The reason Key-Value Coding works this way is because the type system can't represent an argument that's sometimes an object and at other times a primitive value. So KVC always deals with objects and just autoboxes primitive values as necessary. Similarly, if you do [yourObject valueForKey:#"isEditMode"], you'll get back an NSNumber object wrapping the real value.
The correct syntax to set a property is just
self.isEditMode = NO;
If you want to use -setValue:forKey: you'd have to write it as
[self setValue:[NSNumber numberWithBOOL:NO] forKey:#"isEditMode"];
However, there's absolutely no reason to do this in your situation.
That said, since you're in an init method, I would strongly recommend avoiding any property access whatsoever and instead using the ivar directly, as in
isEditMode = NO;
This avoids the possibility of an overridden setter being called (either in this class or a subclass) that makes the assumption that the object has already completed initialization. For this same reason you also want to avoid property access inside of -dealloc.
You can just assign the value directly:
isEditMode = NO;
I think you mean:
self.isEditMode = NO;
If your code does indeed compile (I'm pretty new to Objective-C so I don't know) setValue probably takes a pointer to a string (#"isEditMode", e.g.) and not some other type (isEditMode, e.g.).