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.
Related
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.
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
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'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.