Consider:
class SomeCppClass {
public:
SomeCppClass() {} ;
~SomeCppClass() {} ;
} ;
#interface Test1 : NSObject
- (id) init ;
#property (strong, nonatomic) NSMutableArray * container ;
#end
#implementation Test1
#synthesize container ;
- (id) init {
if (self = [super init]) {
container = [NSMutableArray arrayWithCapacity:10] ;
[container addObject:[NSValue valueWithPointer:new SomeCppClass()]] ;
}
return self ;
}
- (void) dealloc {
for (NSValue * v in container) {
SomeCppClass * c = (SomeCppClass *) [v pointerValue] ;
delete c ;
}
}
#end
Is this the correct approach to delete C++ land objects when you're done with them under ARC?
This will work, but you may consider a couple of other approaches to avoid the NSValue:
Create an ObjC wrapper that manages a single instance of SomeCppClass (and deletes just that one object in its dealloc). This can make them a bit easier to deal with in many cases (automatically converting std::string to NSString in the accessors, etc.) This is basically what NSValue is doing for you, but you get much more flexibility by creating your own custom class. This is usually my preferred approach.
Store the C++ objects in a C++ container such as vector and then you just have to delete the vector and it's easier to pull things out. You can used shared_ptr to put non-copyable objects into a vector. It is understandable if you don't want the overhead of STL and shared_ptr, but they are readily available in Cocoa.
Related
I want to change the properties of some objects (Labels, Buttons..), I created using the Storyboard out of c++-code. So I need a way of running ViewController-class-internal methods.
Is there any proper way to do this? Is there another possibility?
I've tried using callbacks, but there is always this barrier between global and internal in the ViewController-class. Thanks in advance!
EDIT:
Since I don't know how to access a swift class out of c++ code, i cannot give any proper examples, but I thought of something like this (pseudo code):
In c++:
int main(){
say_hello();
}
and in Swift:
class ViewController: NSViewController {
#IBOutlet weak var label: NSTextField!
func say_hello(){
label.stringValue = "Hello"
}
}
Here is an oversimplified example of how this could be done using an Objective-C++ wrapper, as suggested by Richard. Memory management and thread safety aspects, and many other things, are not addressed here. In this example there is a 1-to-1 relationship between Swift and C++ class instances. Also, Swift object pointers are used as identifiers to decide which Swift object should receive a notification. This is kind of dangerous, see comments in the code below. Using more sophisticated data structures in the Objective-C++ wrapper to maintain a connection between Swift and C++ objects, one could easily work around this danger and support relationships other than 1-to-1.
First of all, here is a C++ class that triggers changes in Swift code:
typedef void (*cb_t)(const char *, void *);
class MyClassCPP {
public:
MyClassCPP(cb_t callBack, void * p) : myCallBack(callBack), clientPtr(p) {}
void doWork(); // perform some work and invoke the callback
private:
cb_t myCallBack;
void * clientPtr;
};
void MyClassCPP::doWork() {
myCallBack("C++ code at work...", clientPtr);
}
Here is an Objective-C++ wrapper interface that should be made visible to Swift code via the bridging header, directly or indirectly. Please note that it does not reference any C++ types.
#class SwiftClass; // forward declaration
// can't include *-Swift.h in a header
#interface OCWrapper : NSObject
-(instancetype)init:(SwiftClass * )sc;
-(void)requestWorkFromCPP;
#end
And here is the wrapper implementation. It does reference C++ types. We cannot provide a Swift global function as a callback to C++ code, but we can provide an Objective-C++ global function for this purpose.
// Extension that deals with C++ specifics that can't be visible to Swift
#interface OCWrapper ()
{
MyClassCPP * myClassCPP;
}
#end
void callBack(const char * msg, void * swiftClient)
{
// Danger: what if swiftClient does not point to a SwiftClass instance?
[(__bridge SwiftClass*)swiftClient sayHello:
[[NSString alloc] initWithBytes: msg length:strlen(msg)
encoding:NSASCIIStringEncoding]];
}
#implementation OCWrapper
-(instancetype)init:(SwiftClass * )sc
{
myClassCPP = new MyClassCPP(callBack, (__bridge void*)sc);
return self;
}
-(void)requestWorkFromCPP{
myClassCPP->doWork();
}
#end
The above should be in an Objective-C++ file. Create an Objective-C file and then rename it to have the .mm extension. You will also need to include the *-Swift.h header, so Objective-C++ can use Swift types.
Finally, here is some Swift code that uses the C++ code via the Objective-C++ wrapper:
// This is like your Swift view controller
class SwiftClass : NSObject
{
var label = "[Empty]"
var name : String;
init(name : String) {
self.name = name
}
func sayHello(greeting : String) {
label = "SwiftClass named " + name + " received greeting: " + greeting
}
}
...
let sc = SwiftClass( name : "Zero")
let ocWrapper = OCWrapper(sc)
let sc1 = SwiftClass( name : "One" )
let ocWrapper1 = OCWrapper(sc1)
ocWrapper1.requestWorkFromCPP()
print("The label value from C++: \(sc1.label)")
ocWrapper.requestWorkFromCPP()
print("The label value from C++: \(sc.label)")
...
Consider the following base classes:
#interface ViewBase : UIView
#property (readonly) LayerBase *myLayer;
+ (Class)myLayerClass; // _myLayer = [[[self class] myLayerClass] new];
#end
#interface LayerBase : CALayer
#property AbstractGrid *grid;
#end
class AbstractGrid
{
public:
int rows, columns;
virtual someMethod() = 0;
}
I have a template Grid class that uses different cell types (AbstractGrid is needed because it's not possible to create template Objective-C classes):
template <class Cell>
class Grid : public AbstractGrid
{
public:
Cell **cells;
virtual someMethod() {}
}
Now I want to create a subclass of ViewBase that has type of myLayer also subclass of LayerBase (the +myLayerClass method is also redefined) and use different template parameter for the model class, for example:
#interface AView : ViewBase
#property (readonly) ALayer *myLayer;
#end
#interface ALayer : LayerBase
#property Grid<GridCell> *grid;
#end
class GridCell
{
public:
int row, column;
}
The application works fine with this approach, but compiler gives me warnings about incompatible property types:
property type 'ALayer *' is incompatible with type 'LayerBase *' inherited from 'ViewBase'
property type 'Grid *' is incompatible with type 'AbstractGrid *' inherited from 'LayerBase'
While I can silence the first warning by declaring layer property with type id (which isn't the best solution as I can't use dot syntax without type casting, and I may make mistakes which compiler won't be able to catch):
#property (readonly) id myLayer;
I can't do the same with C++ type. Declaring the grid property as void * also doesn't help.
So is there a proper way to handle such situation? Or I should simply silence the warnings using pragmas since I know what I'm doing?
Please refrain from advising not to use C++ classes because it's not an option (I'm creating a set of cross-platform model classes to ease porting in future).
Yes. Don't alter the return type. For example:
#interface LayerBase : CALayer
- (AbstractGrid *)grid;
#end
#interface ALayer : LayerBase
// ALayer's local storage and typed interface:
#property Grid<GridCell>* grid_GridCell; // << use unique selector names
// ALayer's abstract interface/overrides:
- (AbstractGrid *)grid; // << returns self.grid_GridCell
#end
Well, I decided to remove the grid #property (since I don't need any property features like KVO in this case) in favor of good old getter/setter and simply cast return type in subclasses. clang is clever enough to allow dot syntax for arbitrary getters, so now I'm getting no warnings.
#interface LayerBase : CALayer {
AbstractGrid *_grid;
}
- (AbstractGrid *)grid;
- (void)setGrid:(AbstractGrid *)grid;
#end
#implementation LayerBase
- (AbstractGrid *)grid {
return _grid;
}
- (void)setGrid:(AbstractGrid *)grid {
_grid = grid;
}
#end
#interface ALayer : LayerBase
- (Grid<GridCell> *)grid;
#end
#implementation ALayer
- (Grid<GridCell> *)grid {
return (Grid<GridCell> *)[super grid];
}
#end
Suppose I have two objective c++ objects that each wrap a native c++ object given:
A, B = objective c++ object types
Acpp, Bcpp = c++ object types
In B.mm
#import "Bcpp.h"
#import "B.h"
#interface B ()
{
Bcpp myBcpp; // declare instance c++ variable of type Bcpp
}
#end
In A.mm
#import "Acpp.h"
#import "A.h"
#interface A ()
{
Acpp myAcpp; // declare instance c++ variable of type Acpp
}
#end
#implementation A
// method to return an instance of B from an instance of A (self)
- (B)GetBfromA
{
Bcpp *bfroma = myAcpp.GetBfromA(); // return c++ object
// How do i find the objective C++ object B from its wrapped c++ instance bfroma?
}
#end
The reason for doing this is we have a mature c++ data structure and we wish to wrap it with objective c++ objects. Is the the best way? And if it is, how do we solve the reverse mapping problem?
EDIT: Thank you to the early responders but I have a more tricky situation that I implied above. Suppose the function GetBFromA() returns an instance of Bcpp that had already been declared (as an instance variable of an instance of B). So I am holding a pointer to a Bcpp object that is itself an instance variable of an objective C++ object of type B. How do I find the instance of B from the instance of Bcpp?
What you probably need to do is to be able to create a B from a Bcpp. So B will need to be amended to have an -initWithBcpp: method:
- (id)initWithBcpp:(Bcpp*)bcpp
{
self = [super init];
if (self != nil)
{
myBcpp = *bcpp;
}
return self;
}
Then, in GetBFromA, you'll need to create a B from the Bcpp*:
- (B*)GetBfromA
{
Bcpp *bfroma = myAcpp.GetBfromA(); // return c++ object
B* result = [[B alloc] initWithBcpp:bfroma];
return result;
}
I am developing a iphone game app using Cocos2d.
I find this is an excellent solution to my problem of storage an integer to the sprite's property:
sprite.userData = 123;
However, sprite.userData can store only one piece data. If I need to store three pieces of data, what is the best way of doing it?
'userData' is actually void pointer not retained by the 'Node' class:
void *userData_;
As such it can points to any data structure or class (or even a C function).
Your best bet, for one or nine custom variables, is to subclass CCSprite and have your custom variables as class variables of the new class, using properties to read/write them publicly.
Like a well-versed Objective-C programmer would do.
To explicitly answer your question: You can set an NSDictionary or NSArray to userData, which themselves can contain more than one item. But if you're going that far: read above.
Extend the ccsprite class into userdata class and use that class for any no of variables you want to create... however you will need to use that class for all checks and conditions ....
Here take the code
Userdata.h
//
// UserData.h
//
//
#import "CCSprite.h"
#import "Constants.h"
#class GameLayer;
#interface UserData : CCSprite {
int userDataType;
int tag;
int parentTag;
GameLayer *gameLayer;
BOOL readyForDeletion;
}
#property(nonatomic) int userDataType;
#property(nonatomic) int tag;
#property(nonatomic) int parentTag;
#property(nonatomic, assign) GameLayer *gameLayer;
#property(nonatomic) BOOL readyForDeletion;
-(id) initWithSpriteName:(NSString *)spriteName;
#end
and the userdata.mm
//
// UserData.mm
//
//
#import "UserData.h"
#implementation UserData
#synthesize userDataType;
#synthesize tag;
#synthesize parentTag;
#synthesize gameLayer;
#synthesize readyForDeletion;
-(id) initWithSpriteName:(NSString *)spriteName {
if (self = [super initWithFileName:spriteName]) {
}
return self;
}
-(void) onExit {
[super onExit];
}
-(void) dealloc {
[super dealloc];
}
#end
I'm implementing callback routines for external static C++ library to be used in Objective-C project. Now I have trouble moving data between callback and normal routines. As you can see below my "msgStore" is defined as part of MyMessage class and can be used within class routines such as init(). However attempting same from callback routine, which is NOT part of the MyMessage class, fails.
#interface MyMessage : NSObject {
NSMutableArray *msgStore;
}
#property (nonatomic, retain) IBOutlet NSMutableArray *msgStore;
// Callback functions declarations
void aCBack (const std::string text);
#implementation MyMessage
#synthesize msgStore;
- (id)init
{
if ((self = [super init])) { }
if (msgStore == nil) {
msgStore = [NSMutableArray arrayWithCapacity:100];
}
return self;
}
void aCBack (const std::string text)
{
NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
[msgStore insertObject:msg atIndex:0];
}
The last code line gives error message 'msgStore' was not declared in this scope. I'm guessing it's because aCBack is a plain C function and thus does not have automatic "self" pointer?
Any ideas how to save data received in callback for use inside Obj-C class?
You cannot call msgStore from the function because it is not in the scope of the function.
There are a few ways to get to it in the function.
One is to use a singleton class. If you plan on only using one message store, then you can make that class a singleton. That means you can get the object instance of that class by calling a class method, which you can do from any scope. See also: What should my Objective-C singleton look like?
MyMessage * myMsg = [MyMessage sharedMessage]; // this will get you a pointer to the shared instance
Another way is, if the callback function allows, you can also pass it as a void * data argument, then cast it to a MyMessage in the function. See also Alex Deem's answer.
PS. You create the array with [NSArray arrayWithCapacity:], which you might want to make [[NSArray arrayWithCapacity:] retain] or just [[NSArray alloc] initWithCapacity:], so the object won't vannish on the next autoreleasepool housekeeping round.
The simple solution is to pass a pointer to the MyMessage object as an argument of the callback function. Something like:
void aCBack( MyMessage * message, const std::string text)
{
NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
[message.msgStore insertObject:msg atIndex:0];
}
Let's be clear: Singleton is the answer (at least one good choice). However deadline is hanging on my neck and I just got to get something more or less working today. Here's what I use, for here and now. Please note this is NOT a "singleton":
#interface MyMessage : NSObject {
NSMutableArray *msgStore;
}
#property (nonatomic, retain) NSMutableArray *msgStore;
- (void)myStoreMessage: (NSString *) msg;
#end
// Callback C/C++ function declarations
void aCBack (const std::string text);
MyMessage *myObserver = nil;
void aCBack (const std::string text)
{
NSString *msg = [[NSString alloc] initWithCString:(const char *)text.c_str() encoding:NSUTF8StringEncoding];
[myObserver myStoreMessage:[NSString stringWithString:msg]];
[msg release];
// TODO: fix memory leak
}
#implementation MyMessage
#synthesize msgStore;
- (id)init
{
if ((self = [super init])) { }
if (msgStore == nil) {
msgStore = [[NSMutableArray arrayWithCapacity:100] retain];
}
myObserver = self;
return self;
}
- (void) myStoreMessage: (NSString *)msg
{
[self.msgStore insertObject:msg atIndex:0];
}
#end
What can I say, it seems to work. There is a memory leak with msg, which I haven't figured out, but otherwise this is what I'll be using for the demo. When there is time afterwards (yeah, sure) I'll implement a proper Singleton.