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.
Related
I'm working on a project that uses OpenCV, which has forced me to branch out a little bit into C++ and Objective-C. I built a small Objective-C class that calls Objective C++ functions from the OpenCV framework, and bridged that into Swift.
Inside the objective-c class, I've got a handful of NSInteger members that I want to be able to change from UI. But when I try to get or set them, it crashes with the familiar unexpectedly found nil while unwrapping an Optional value.
I have no doubt that it's an elementary mistake, but I've been spinning my wheels for awhile and am having trouble narrowing it down. If somebody could take a look at my class and tell me what's wrong that would be great.
.h file
#interface OpenCVWrapper : NSObject
// This is a singleton for providing global access to the OpenCV Wrapper
+ (OpenCVWrapper *)sharedInstance;
- (UIImage *)processImageWithOpenCV:(UIImage*)inputImage;
- (void)setupVideoCamera:(UIView*) parentView;
// Filtering properties
#property (atomic, assign) NSInteger hMin;
#property (nonatomic, assign) NSInteger hMax;
#property (nonatomic, assign) NSInteger sMin;
#property (nonatomic, assign) NSInteger sMax;
#property (nonatomic, assign) NSInteger vMin;
#property (nonatomic, assign) NSInteger vMax;
#ifdef __cplusplus
#property (nonatomic, retain) CvVideoCamera* videoCamera;
#property (nonatomic, retain) VideoHandler* videoHandler;
#endif
#end
.mm file
#implementation OpenCVWrapper : NSObject
#synthesize videoHandler;
#synthesize videoCamera;
#synthesize hMin;
#synthesize hMax;
#synthesize sMin;
#synthesize sMax;
#synthesize vMin;
#synthesize vMax;
// This is how a singlegton is created in Objective-C (or so I'm told)
static OpenCVWrapper *sharedInstance = nil;
+ (OpenCVWrapper *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
// Initialize the variables
// hue is from 0 to 180, saturation and value go from 0 to 255
-(id) init {
self = [super init];
self.hMin = 0;
self.sMin = 0;
self.vMin = 0;
self.hMax = 180;
self.sMax = 255;
self.vMax = 255;
return self;
}
I then have bridging header that imports the .h file, and the compiler recognizes OpenCVWrapper.sharedInstance, but if I try to get or set sharedInstance.hMin it finds an unexpected nil and crashes.
Any pointers would be greatly appreciated!
The problem turned out to be unrelated to these classes. There was a dangling storyboard reference that was causing the crash.
#IBOutlet weak var slider1:UISlider!
#IBOutlet weak var slider2:UISlider!
#IBAction func sliderChanged(sender:UISlider) {
switch sender {
case slider1: OpenCVWrapper.sharedInstance().hMin = NSInteger(sender.value)
case slider2: OpenCVWrapper.sharedInstance().hMax = NSInteger(sender.value)
}
}
The sliderChanged function was getting called, so I overlooked the fact that I forgot to hook up the outlets and assumed the nil had something to do with accessing the sharedInstance.
Thanks to everyone who took time to look over my code and offer suggestions.
I'm trying to integrate Custom C++ class in an Objective C class this way:
C++ class header
class Analyzer
{
public:
Analyzer(std::string const& face_cascade_url, std::string const& eyes_cascade_url, std::string const& nose_cascade_url );
};
Objective C header:
#interface cvVideoWrapper : UIViewController <CvVideoCameraDelegate>
#property Analyzer analyzer;
#end
Objective C implementation:
#implementation cvVideoWrapper
-(void) get_ready {
NSString* face_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_frontalface_alt2"
ofType:#"xml"];
NSString* eyes_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_eye_tree_eyeglasses"
ofType:#"xml"];
NSString* nose_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_mcs_nose"
ofType:#"xml"];
self.analyzer = Analyzer([face_filename UTF8String], [eyes_filename UTF8String], [nose_filename UTF8String]);
}
#end
I'm getting at the Objective C implementation this error:
No matching constructor for initialization of 'Analyzer'
How can I fix this?
I'd suggest to use forward declaration here to sanitize your headers. And use the pointer instead of the instance.
Objective C header:
class Analyzer;
#interface cvVideoWrapper : UIViewController <CvVideoCameraDelegate>
#property Analyzer* analyzer;
// ^^ it is a pointer now
#end
And then in Objective C implementation:
#implementation cvVideoWrapper
-(void) get_ready {
NSString* face_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_frontalface_alt2"
ofType:#"xml"];
NSString* eyes_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_eye_tree_eyeglasses"
ofType:#"xml"];
NSString* nose_filename = [[NSBundle mainBundle]
pathForResource:#"haarcascade_mcs_nose"
ofType:#"xml"];
// Create object here with new:
self.analyzer = new Analyzer([face_filename UTF8String], [eyes_filename UTF8String], [nose_filename UTF8String]);
}
// Don't forget to cleanup when you're done:
- (void)dealloc
{
delete self.analyzer;
[super dealloc];
}
#end
Assuming you included Analyzer's header and have the Objective-C implementation compiling as Objective-C++, convert the value returned by UTF8String to a std::string. The compiler can't find a constructor that has the right signature nor for the automatically convertible values of the UTF8String return type.
std::string([face_filename UTF8String]) et al should work.
I don't know how to tie ObjC with C++, but it looks to me like the problem is that you're storing an Analyzer by value but it doesn't have a default constructor. In C++ that would mean you'd need to provide the arguments for construction in the initializer list of the constructor for the containing class. If that can't be done then you'd have to store by pointer.
It also looks like Analyzer doesn't have an assignment operator so that would be a problem too.
Is it possible to access b2Fixture and b2Body properties in one class in another class in such a way that joints could be created. If yes, how can it be done. Please Help
Create property to get access to your b2Body object. Then you can get list of b2Fixtures for this b2Body.
in your .h file
#interface MyClass
{
b2Body* m_body;
}
#property (nonatomic, readonly) b2Body* body;
#end
in your .mm file (you must use .mm extension to be able to use c++ classes and methods)
#implementation MyClass
#synthesize body = m_body;
- (id) init
{
self = [super init];
if( self != nil )
{
// create your b2Body here and
}
return self;
}
#end
After this all MyClass instances will have property body, that can be accessed as
myClassInstance.body
or
[myClassInstance body];
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 have a cpp class like that..
class MyContactListener : public b2ContactListener
{
int countContact;
///this is an objective c class...
HelloWorld *hel;
public:
void EndContact(b2Contact* contact)
{
///initialize objective c object
hel=[[HelloWorld alloc] autorelease];
///call objective c method.........
[hel beginContact];
}
};
inside cpp class i call a objective c method.the objective c method looks like..
-(void )beginContact
{
shakeCounter++;
[_label setString:[NSString stringWithFormat:#"%d",shakeCounter]];
}
The objective c method get called....and also the variable shakeCounter increased.....but
_label string is not updated...._label is initialized properly and work properly if i called
the objective c method from objective c class using self....
Can anyone help???
Use a Delegate in the Contact listener to call the methods on the objective-c class:
class MyContactListener : public b2ContactListener
{
public:
MyDelegateClass *delegate;
void BeginContact(b2Contact* contact) {
[delegate beginContact:contact];
}
void EndContact(b2Contact* contact) {
[delegate endContact:contact];
}
void PreSolve(b2Contact* contact, const b2Manifold* oldManifold) {
[delegate preSolve:contact manifold:oldManifold];
}
void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse) {
[delegate postSolve:contact impulse:impulse];
}
};
Declare a Protocol for the Delegate:
#import "Box2D.h"
#protocol B2ContactListener <NSObject>
-(void) beginContact:(b2Contact*) contact;
-(void) endContact:(b2Contact*) contact;
-(void) preSolve:(b2Contact*) contact manifold:(const b2Manifold*) oldManifold;
-(void) postSolve:(b2Contact*) contact impulse:(const b2ContactImpulse*) impulse;
#end
Declare the interface that implements the protocol:
#interface MyDelegateClass: CCLayer <B2ContactListener> {
//interface code here.
}
#end
#implementation MyDelegateClass
-(void) beginContact:(b2Contact*) contact {
//implement your code here
}
-(void) endContact:(b2Contact*) contact {
//implement your code here
}
-(void) preSolve:(b2Contact*) contact manifold:(const b2Manifold*) oldManifold{
//implement your code here
}
-(void) postSolve:(b2Contact*) contact impulse:(const b2ContactImpulse*) impulse{
//implement your code here
}
#end
Construct the delegate and the assign it in your code. Set it in your world object:
MyContactListener *listener = new MyContactListener();
listener->delegate = self;
world_->SetContactListener(listener);
Now your objective-c class will receive the events.
oww....i solved it....
just put this in top of project
#define PTM_RATIO 32
#import "HelloWorldScene.h"
id refToSelf;
and initialize this in onload or something like that...
self = [super init];
refToSelf = self;
Now call the objective c method using this....
[refToSelf beginContact];
it will work...........
I'm not sure if it is the source of your problem, but this line:
hel=[[HelloWorld alloc] autorelease];
Should be:
hel=[[[HelloWorld alloc] init] autorelease];
Your code is totally confused. I assume that you want to create the Objective-C object inside endContact and persist it until some later point. At the moment you are creating a new object each time but you are not initialising it at all. Your code is never going to work.
The C++ method should probably look something like:
void EndContact(b2Contact* contact)
{
// release old object and initialize a new one
[hel release];
hel = [[HelloWorld alloc] init];
///call objective c method.........
[hel beginContact];
}
or
void EndContact(b2Contact* contact)
{
HelloWorld* hel = [[HelloWorld alloc] init];
[hel beginContact];
[hel release];
}
Depending on how long you want your hel object to last for.
I don't know why _label is not being updated. You'll need to show us your code for initialising it to answer that.
Few things:
Is _label an NSString, or an NSMutableString? An NSString won't respond to setString, because it is fixed at creation.
This:
self = [super init];
refToSelf = self;
Is odd, to say the least. You're basically saying self = self here. You never need to call init on your self.
I'd strongly recommend having a look at Apple's excellent intro here: The Objective C Language