I was just starting a new iOS project with ARC and got stuck when I tried to cast my (id)sender of an IBAction to a Objective-C pointer. XCode was complaining: Cast of Objective-C Pointer to UIButton* is idsallowed with ARC. How do I do that correclty when using ARC?
- (IBAction)didPressButton:(id)sender
{
UIButton *button = (UIButton*) sender;
}
It seems that the cast simply is unnecessary. Is it ok just to assign sender to a new pointer like that?!
UIButton *button = sender;
Cheers
you can always use - (IBAction)didPressButton:(UIButton *)sender {...
Related
I have a set of buttons in a group:
buttons = new QButtonGroup();
button_0 = new MyButton("A", this);
button_1 = new MyButton("B", this);
button_2 = new MyButton("C", this);
MyButton is a QPushButton, which inherits QAbstractButton. One MyButton in the group can be selected at a time.
MyButton::MyButton(const QString &text, QWidget *parent)
: QPushButton(text, parent)
{
this->setCheckable(true);
this->setChecked(false);
}
MyButton has an increment method.
void MyButton::increment()
{
this->j++;
}
I want to call the increment method on whichever MyButton instance is currently selected. There's a handy checkedButton() method that I can call on buttons, but that returns a pointer to a QAbstractButton.
I can't call increment on the pointer or the QAbstractButton. buttons->checkedButton()->increment(); yields error: no member named 'increment' in 'QAbstractButton'. How can I increment the selected MyButton?
You just need to let the compiler know that you are using a MyButton* rather than a QAbstractButton*, and that means a cast.
If you know that the pointer is a MyButton* then you can just do a static_cast<MyButton*>(buttons->checkedButton())->increment(); If you don't know if its a MyButton* then you'll need to check, and that'll mean something like this:
MyButton* temp = dynamic_cast<MyButton*>(buttons->checkedButton());
if(temp != nullptr) temp->increment();
EDIT:
SaZ has pointed out that qobject_cast is preferable to dynamic_cast because:
It doesn't require RTTI support and it works across dynamic library boundaries
MyButton already inherits from QObject, but there is one more qualification, that it is declared with Q_OBJECT. If both of these qualifications are met then you can simply replace the dynamic_cast with a qobject_cast:
MyButton* temp = qobject_cast<MyButton*>(buttons->checkedButton());
if(temp != nullptr) temp->increment();
Note that in the case of QObject derived classes, you can and should use qobject_cast() instead of dynamic cast. It is more efficient by employing the Qt meta system.
Also, unlike the dynamic cast, it will work when compiling without RTTI, and with dynamic libraries as well.
Dynamic cast QAbstractButton to MyButton and call increment().
QAbstractButton* abstractButton = buttons->checkedButton();
MyButton* myButton = dynamic_cast<MyButton*>(abstractButton);
if (myButton)
{
myButton->increment();
}
For the sake of completeness, in the case of Qt program you may also make use of signal-slot system: if increment was declared to be a slot then this would work:
QMetaObject::invokeMethod(abstractButton, "increment");
Hello I know that this problem was discussed several times, but I have feeling that no other explenation works for me. Maybe it isn't possible but... at the moment I have mm file it looks like (init methods/ header file) obj-c except few C++ methods (library live555 which I use is written in C++), and I can call C++ methods form obj-c just fine. But when I want call obj-c in c++... then I got error. I know self isn't know. But how I can move aroud it? I try with 2-3 tuts but all of them assume that obj-c method isn't called inside that obj-c++ class.
My Obj-C++ class (.mm file)
#interface testSender : NSObject{
#private
NSMutableArray *_buffors;
}
+(id)voiceSender;
//Function to invoke
-(bool)continueSendBuffer;
#end
#implementation testSender
#synthesize address=_address,port=_port;
UsageEnvironment* env;
+(id)voiceSender{
return [[self alloc]init];
}
- (id)init
{
self = [super init];
if (self) {
[self initRTPProt];
_buffors = [NSMutableArray array];
}
return self;
}
-(void)initRTPProt{
//init protocol
}
void afterPlaying(void* clientData); // forward
void afterPlaying(void* /*clientData*/) {
// We and stream and now I want check if it's some other streams to send so normaly I should call
[self continueSendBuffer];
}
-(bool)continueSendBuffer{
if ([_buffors count] == 0) return false;
NSData *nextBuffer = [_buffors objectAtIndex:0];
[_buffors removeObjectAtIndex:0];
[self sendNextBuffer:nextBuffer];
return true;
}
-(void)sendNextBuffer:(NSData*)buffer{
// Send next buffer
/setting sessionState..
// Start the streaming:
*env << "Beginning streaming...\n";
// Method afterPlaying will be called after ther will be nothing to send
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, NULL);
env->taskScheduler().doEventLoop();
}
#end
At c++ method void afterPlaying(void*) when I want use [self continueSendBuffer] then I got error Use of undeclared identifier 'self'.
Solved
Chuck did great job and explain me how Objective-C++ works. Read it!
Basically you just can't call obj-c method in "c++ method" (I know there are free floating methods) with out passing to them self pointer.
I have to modify a bit startPlaying call and now in third argument I pass self pointer
Also method afterPlaying had to been change so now she can use clientData pointer. Throught this I can call continueSendBuffer.
startPlaying
sessionState.sink->startPlaying(*sessionState.source, afterPlaying, (__bridge void*)self);
afterPlaying
void afterPlaying(void* clientData) {
[(__bridge NXVoiceSender*)clientData continueSendBuffer];
}
Objective-C++ does not unify C++ classes and Objective-C classes. It allows you to mix Objective-C code and C++ code, but the two object models are still completely separate — it basically just uses C++ where C would be used in normal Objective-C. What you're calling "C++ methods" there are actually just free-floating functions, because an Objective-C class definition isn't a special context in C++. They are not methods of the class.
You'll somehow need to keep a pointer to the object in question and get that into the afterPlaying() function (and also keep the object alive long enough to be referenced when afterPlaying() is called). I'm not familiar with the library, but that clientData parameter looks promising.
The type of the second argument of startPlaying is std::function? If so, you can pass a Block instead of C++11 lambda for that as the following with "C++ Language Dialect" to "C++11".
__weak testSender *self_ = self;
sessionState.sink->startPlaying(*sessionState.source, ^(void *){
// We and stream and now I want check if it's some other streams to send so normaly I should call
[self_ continueSendBuffer];
}, NULL);
I have a C++ project on iOS. It mostly uses C++, except for some tasks that require Objective-C. For instance, showing a UIAlert.
So I call the UIAlert from C++. How do I get the result and know what was the button tapped by the user?
This is the implementation of the C++ class calling to Objective-C
void iOSBridge::iOSHelper::ShowAlert()
{
[IsolatedAlert showAlert];
}
And here I have the Objective-C implementation:
+ (void)show{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message: #"hello"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
[alert show];
[alert release];
}
+ (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
}
Is there any way to call C++ again from clickedButtonAtIndex delegate?
Thanks.
There's nothing preventing you from calling a C++ class from an Objective C one. You'll need to provide your Objective C class with some sort of a handle to the C++ class, which it will need to store as an instance variable. Then you can do whatever you want with it.
That's going to be awkward to accomplish while you're only using class methods like you are. It'll be better to use instance methods, and then create an instance from the C++ side, provide the instance with a handle, then send messages to the instance instead of the class.
Make the extension of this class as .mm
Then have an static var YourClaas *delegate; in it
+ (void)showAlertWithDelegate:(YourClass*)del{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message: #"hello"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
delegate = del;
[alert show];
[alert release];
}
+ (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
del->buttonClickAtIndex(buttonIndex);
}
and define void buttonClickAtIndex(int index) method in your cpp file
How do you handle the setAction message in Objective-C++? (Not Objective-C.)
For example, suppose I have:
my_class.mm
NSSegmentedControl *segmented = [[NSSegmentedControl alloc] init];
[segmented setSegmentCount:5];
// etc.
[segmented setAction:???];
Application: I am programming in Qt (C++) and need a wrapper around some Cocoa widgets I want to use directly. I am inheriting from QMacCocoaViewContainer but can't figure out how to handle the "clicks" of the NSSegmentedControl I am wrapping. Eventually this will emit a standard Qt signal.
action is just a selector - it is used in tandem with target. so write an objc method for target+action which calls through or does what you really want. actions' arguments are the sender, but you can omit that if you don't need it. the sender will be whatever is sending the message (e.g. the control). it's no different in ObjC++ - this has to be wrapped in an objc method because the target must be an objc object.
so it would look like this:
obj.action = #selector(pressDoStuff:);
and the method is:
- (void)pressDoStuff:(id)sender
#Justin has the right answer; I'll accept it, but also include the final solution in case it helps others. The trick is you need a proxy class, as #smparkes noted.
Ignoring the .h files for brevity:
mac_control.mm
MacControl::MacControl(QWidget *parent) : QMacCocoaViewContainer(NULL, parent) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSSegmentedControl *segmented = [[NSSegmentedControl alloc] init];
// Set up NSSegmentedControl...
// The proxy class marshalls between Objective-C and C++.
proxy_ = [[MacControlProxy alloc] init];
[proxy_ setTarget:this];
[segmented setTarget:proxy_];
[segmented setAction:#selector(handleToggle:)];
setCocoaView(segmented);
[segmented release];
[pool release];
}
MacControl::~MacControl() {
delete proxy_;
}
void MacControl::TriggerAction(int index) {
// Trigger the action in Qt/C++.
}
mac_control_proxy.mm
#implementation MacControlProxy
- (id)init {
[super init];
target_ = NULL;
return self;
}
-(void) handleToggle: (id)sender {
if (target_) {
target_->TriggerAction([sender selectedSegment]);
}
}
-(void) setTarget: (MacToolbarButtonControlImpl*)target {
target_ = target;
}
#end
I'm following up on Dave Mateer's answer (which was super helpful!).
I was having issues setting the C++ target (from within a objective-C++ class) and used [NSValue valueWithPointer:theTargetCxxClass] to pass the target to the Proxy.mm class.
So, inside of my Objective-C++ class, rather than doing this:
[proxy_ setTarget:this];
I did this:
[proxy_ setTarget:[NSValue valueWithPointer:this]];
or
[proxy_ setTarget:[NSValue valueWithPointer:ptrToMyCxxObject]];
And doing this got rid of an error about passing a C++ class (which does not extend type "id") to the Objective-C++ proxy class.
Inside of the proxy class, you then need to use NSValue's pointerValue method and then cast back to the C++ class in order to send a message to it.
-(void) myButtonAction: (id)sender {
((MyCxxClass*)[target pointerValue])->someMethodInMyCxxClass();
}
I first was alerted to the "valueWithPointer" trick in this post.
I'm writing a QT app and I'm very rusty with C++, so I'm guessing that's the problem. I've got a crash with an exc_bad_access signal on my Mac, which means I'm doing something wrong with memory. Here's my code:
void MainWindowController::showMainWindow() {
MainWindow *w = mainWindow();
w ->show();
}
MainWindow *MainWindowController::mainWindow() {
if (NULL != _mainWindow)
return _mainWindow;
// otherwise, we need to load it and return it
_mainWindow = new MainWindow(0);
return _mainWindow;
}
_mainWindow is an instance variable, a pointer (as you might have guessed from the function signature). It's a simple lazy-loading. I think I'm doing memory management OK, as this class owns the object (which is later deleted in the destructor).
The crash occurs on the w -> show(); line, QT complains its somewhere inside the QWidget show() function, which doesn't really make sense to me.
Can somebody help me out? Thanks!
Turns out it was something even simpler. I'm used to Objective-C, where ivars are automatically initialized to 0. C++ doesn't do this. So, I had to make sure _mainWindow was initialized to NULL in the constructor. Problem solved.