Passing struct from ObjC to Swift - ios

Trying to use a struct from ObjC to Swift doesn't seem to be that easy. I end up getting a Unsafe pointer that I don't know if I can cast reliably.
Here is the code:
//
// In OBJC land
//
// Type declared as a struct
typedef struct node {
int children_count;
} node_t;
// Super class has a property
#property (nonatomic, assign, readonly) node_t *node;
//
// In SWIFT land
//
// Derived class tries to set the property inside the C struct
let n: UnsafeMutablePointer<node_t> = super.node // As swift compiler sees it
n.children_count = 0 // ERR!!!
Do I really need to apply unsafeBitcast here or is there a simpler/safer and more elegant way to convert what seems to be a frequent scenario?
UPDATE:
I tried using memory to access the elements of the struct and I am getting a EXC_BAD_INSTRUCTION
var node: node_t = self.node.memory
node.children_count = 42
UPDATE CONT'D & FINALE
I got it to work. Thanks to #matt's patience for making sure I groked 'memory' access completely. The other trick is to realize the assignment in one continuous statement like this:
var node: UnsafeMutablePointer<node_t> = self.node
node.memory.children_count = 42
If I do the following, the change doesn't get committed passed the function call:
var node: node_t = self.node.memory
node.children_count = 42

One problem is that this is not a "frequent scenario". In fact, this is a very odd thing to do:
#property (nonatomic, assign, readonly) node_t *node;
This thing (node_t) is a C struct. So why do you want a pointer to it? Perhaps you have no choice about this, but far and away the usual thing is simply to have the struct itself as a property:
#property (readonly) node_t *node;
In that way, the property arrives as a Swift struct called node_t, and if you have a var reference to it (not a let as you've written it), you can assign into its children_count directly, as you would expect.
If you insist on having a pointer, then you will have to dereference the pointer, won't you? You are not doing that. To do so, take the pointer's memory:
n.memory.children_count = 0

Related

Best way to pass CGFloat by reference to another class

In ClassA, I have a CGFloat value x that I want to pass by reference to ClassB such that if I make a change to the CGFloat in ClassA, it will be reflected in the reference to x in ClassB. Also, when I pass it to ClassB, I want to store it as a property.
I've thought about using a CGFloat pointer, but I'm struggling to figure out the proper syntax to make it a property:
#property(nonatomic) CGFloat *x;
And then to dereference it:
self->x
I thought about using NSNumber but there is no way to set the value using NSNumber such that it will update in ClassB. I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
What is the best pattern to go about doing this?
I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
The advantage of this approach is safety, you create an object, both classes reference it, and ARC takes care of the memory management.
The class is easy to define, for example:
#interface ABShare1 : NSObject
#property CGFloat x;
#end
#implementation ABShare1
#end
(in a .h & .m file – same for other examples)
A class using this would be something like:
#implementation ClassA
{
ABShare1 *one;
}
...
one = ABShare1.new; // somewhere in initialisation
...
... one.x = 42; ... z = one.x * 24; ...
Note: the above stores the ABShare1 reference in a private instance variable, you can store it in a property if you wish but there is no need to.
You can call a method on another class passing the object, e.g.:
ClassB *myB;
...
[myB using:(ABShare1 *)sharedVariable];
and that other class can keep the reference as long as it requires, memory management is automatic.
I've thought about using a CGFloat pointer
This is the standard C (a subset of Objective-C) way of "passing by reference".
You can store a CGFloat * in a property, all "object" valued properties in Objective-C just store pointers (e.g. #property NSString *name; stores a pointer to an NSString object).
You must create the variable that the CGFloat * references, the equivalent of new or alloc/init in Objective-C. You can use the address of a variable, e.g. something like:
CGFloat actualX;
CGFloat *x = &actualX;
but you have to manually ensure that the referenced variable, actualX, lives at least as long as its pointer, stored in x, is in use – failure to do that results in a dangling pointer.
The other option is to dynamically allocate the storage, the direct equivalent of new, e.g. something like:
CGFloat *x = malloc(sizeof(CGFloat));
However you are now responsible for determining when the storage is no longer required and releasing it (using free()).
The first solution to you is "overkill" – maybe because while you are freed from concerns over memory management you don't get a "variable" but two functions/methods to get/set a value.
The second solution is closest to feeling like a "variable", you just use *sharedVariable rather than sharedVariable. However while the manual memory management required is standard for C programmers, it is not for Objective-C programmers.
A third approach mixes the two building on how structures (struct) in C can be used: to share a collection of variables rather than share each one individually by address, instead define a struct with a member for each variable, allocate one and share its address, something like:
typedef struct ABShare
{ CGFloat x;
CGFloat y;
} ABShare;
ABShare *one = malloc(sizeof(ABShare));
one->x = 42;
one->y = 24;
The above has the same memory management issues as the second solution, but we can convert it to a very close Objective-C equivalent:
#interface ABShare : NSObject
{
#public // required
CGFloat x;
CGFloat y;
}
#end
#implementation ABShare
#end
Note: Objective-C classes are effectively implemented using structs, indeed the first Objective-C compilers actually translated them into C struct code.
Using this is very close to the C:
ABShare *one = ABShare.new;
one->x = 42;
one->y = 24;
Same "variable" look as C but with automatic memory management.
This last scheme is essentially how Objective-C manages sharing variables when a block is created – all the local variables accessed by the block are moved into a dynamically allocated object/struct and the variables then accessed using ->.
Which is best in Objective-C? The first and the third are both "Objective-C" style, the second is usually avoided accept when interacting with C APIs. Of the first and third pick whichever feels "right" semantically, rather than concerns over performance, to you. HTH
[NSMutableData dataWithLength:sizeof(CGFloat)] and cast mutableBytes to CGFloat*
You can implement getter and setter of property #property(nonatomic) CGFloat x -without pointer

#property #synthesize equivalent in swift

I have something like
#property(nonatomic,retain) UIImageView *whiteBfFillUp;
#end
#synthesize locationManager;
I am new to swift coding. Can anyone tell me the equivalent code in swift.
There is no equivalent.
In Swift when you write a varor let in a class or struct declaration you already declaring a property.
Define properties to store values
This is what is written in Swift documentation.
If you are concerned about access control you can use private or public modifiers.
public var somePublicVariable = 0
If you'd like to override properties such as you did in Objective-C you will find useful properties observers such as didSet{} willSet{}.
If you need a a readonly properties you can make the setter private.
public private(set) var hours = 0
If you are only looking for equivalent of property, then you just need to create your class level variables. All class level variables are by default 'strong' or 'retain'. If, however, you want them to be weak then use weak.
This would be something like
var whiteBfFillUp: UIImageView? = nil
The ? at the end means that this is an optional type. If it's not, you would need to assign it some value in the init method, or right there.

Conforming to an Obj-C protocol's property in Swift

I'm implementing a protocol from an Obj-C library in a Swift class that defines two properties as:
#property (nonatomic, assign, getter = isLoading) BOOL loading;
#property (nonatomic, readonly) UIExpansionStyle expansionStyle;
Although I'm not sure how to conform to these requirements in my Swift class.
I've looked at the Obj-C examples, but I haven't gleaned any solutions from that. I've tried declaring class variables with the same name, but that hasn't worked. Any suggestions on how I would go about this?
PS, the library in question is https://github.com/OliverLetterer/SLExpandableTableView
I think you're making this harder than it needs to be. With a protocol defined like this:
#protocol MyProtocol
#property (nonatomic, assign, getter = isLoading) BOOL loading;
#property (nonatomic, readonly) UIExpansionStyle expansionStyle;
#end
the following class conforms:
class Conformer : MyProtocol {
var loading: Bool
var expansionStyle: UIExpansionStyle
init(loading: Bool, expansionStyle: UIExpansionStyle) {
self.loading = loading
self.expansionStyle = expansionStyle
}
}
Prefixing boolean getters with is is a Cocoa convention, so Swift already knows about it and doesn't require you to do anything special on the swift side.
The confusing part might be all the modifiers to the Objective C property declaration. Let's go through them one by one.
nonatomic has no equivalent in Swift, nothing to do here.
assign is automatic for value types, nothing to do here either.
getter = isLoading is a Cocoa convention which Swift understands and needs nothing from you in order to make this work.
readonly you can do this in Swift (just use the get{ } syntax) but it is not necessary. This is because you are allowed to expand on the contract made by the protocol. MyProtocol requires there be a property called expansionStyle that can be read from, it does NOT say that it must not be possible to write to that property in the type that implements the protocol, just like it doesn't say you can't have other properties/methods on that same class.
Lance's answer didn't work for me, this is how I got isLoading to conform to the protocol (Swift 2.2)
var expansionStyle: UIExpansionStyle = UIExpansionStyle(0)
var _loading: Bool = false
var loading:Bool {
#objc(isLoading) get {
return self._loading
}
set(newValue){
_loading = newValue
}
}

iOS, Use struct in Objective C, struct values not assignable from super view

I am trying to use a struct to store three values as a unit so to speak. I am getting an error: "Expression not assignable" when I try to assign values to the struct's values from the object's super view.
Anyone know why this is?
in my class's .h file I have defined the struct and a property
#interface MyClass : UIView
{
struct customStruct {
float a;
float b;
float c;
};
}
#property (assign, nonatomic) struct customStruct myStruct;
from the super view I try to assign a value and I get an error: "Expression not assignable"
object.myStruct.a = someValue;
Try this:
struct customStruct aStruct = object.myStruct;
aStruct.a = someValue;
object.myStruct = aStruct
It is exactly the same situation as not being able to do this:
view.frame.size.width = aWidthValue;
BTW declaring a struct inside a class interface seems like a very bad style. This is much cleaner:
typedef struct {
float a;
float b;
float c;
} customStruct;
#interface MyClass : UIView
#property (assign, nonatomic) customStruct myStruct;
This is because object.myStruct returns a copy of your structure member and there is no point of changing member a of that copy.
You should do get the entire struct change the member and then set the struct member again (using the get/set synthesized methods)
Define it outside/before interface scope:
struct LevelPath{
int theme;
int level;
};
//here goes your interface line
#interface BNGameViewController : UIViewController{
}
//create propertee here
#property (nonatomic, assign) struct LevelPath levelPath;
To add on to the earlier suggestions, I'd suggest to not store your struct as a property, but a member variable. E.g:
#interface MyClass () {
CGPoint myPoint;
}
Then in your class you can assign new values to that struct directly:
myPoint.x = 100.0;
instead of when it's a #property, which is read-only:
myPoint = CGPointMake(100.0, myPoint.y);
Given that this isn't an NSObject subclass, unless you are overwriting the getter/setter methods, I don't see the purpose of making it an #property anymore, since that is mostly useful for ARC to help with your objects' memory management. But please correct me if I'm wrong on that point (ouch).

Objective C: Good way to define C array like MyStruct theArray[18][18]?

I need to use something like a C array:
MyStruct theArray[18][18];
but I cannot define it as a property:
#property (nonatomic) MyStruct theArray[18][18];
then I have to:
#implementation MyClass
{
MyStruct theArray[18][18];
}
But is this good in term of modern Objective C guideline?
Thanks
Update:
I know I can define the struct as class and use NSMutableArray to handle it, but it is more convenient to use the C array in my case, the main concern is coding guideline and memory issue, as I do not allocate or release the theArray[18][18], not sure what its life cycle is, and I'm using ARC.
Properties cannot be of array type, while public instance variables do not provide sufficient encapsulation. A more Objective C - like approach would be defining a private 2D array, and a pair of methods or a method returning a pointer to access it - something along these lines:
// For small structs you can use a pair of methods:
-(MyStruct)getElementAtIndexes:(NSUInteger)i and:(NSUInteger)j;
-(void)setElementAtIndexes:(NSUInteger)i and:(NSUInteger)j to:(MyStruct)val;
// For larger structs you should use a single method that returns a pointer
// to avoid copying too much data:
-(MyStruct*)elementAtIndexes:(NSUInteger)i and:(NSUInteger)j;
How about use pointers instead?
#property (nonatomic) MyStruct **theArray;
The answers so far are great. . . here's two more options:
1. A bit hacky
(I'm not sure if this requires Objective-C++)
You can create the array as a public property like so:
#interface MyClass
{
#public:
MyStruct theArray[18][18];
}
#end
And then access it as follows:
myClass->theArray
2. Return a Struct
While you can't return a C-style array, you can return a struct:
typedef struct
{
CGPoint textureCoordinates[kMaxHillVertices];
CGPoint borderVertices[kMaxBorderVertices];
} HillsDrawData;
#interface Hills : NSObject
{
HillsDrawData _drawData;
}
- (HillsDrawData)drawData; //This will get cleaned up when the class that owns it does.
#end

Resources