NSInvocation invoke giving bad access in iOS 8 - reflection

I'm trying to run this code in iOS 8 but I'm getting a bad access error in the method called, this runs ok in iOS 7. Does anyone has a clue about this?
-(double) calcularColumna:(int ) anio :(int) mes :(NSString * ) columna {
NSInvocation * invocation = [selectores objectForKey:columna];
if(!invocation){
NSString * metodo = [NSString stringWithFormat:#"fondo%#:anio:mes:",columna];
SEL selector = NSSelectorFromString(metodo);
if( ![self respondsToSelector:selector]) {
return -1;
}
invocation = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:selector]];
invocation.selector = selector;
invocation.target = self;
[invocation setArgument:(__bridge void *)(self.valoresEntrada) atIndex:2];
[selectores setObject:invocation forKey:columna];
}
double valor = 0;
[invocation setArgument:&anio atIndex:3];
[invocation setArgument:&mes atIndex:4];
[invocation invoke];
[invocation getReturnValue:&valor];
/* }else {
valor = -1;
}*/
return valor;
}
Thanks for your comments.

[invocation setArgument:(__bridge void *)(self.valoresEntrada) atIndex:2];
is wrong. You need to pass a pointer to the value being passed. Something like
// I don't know the type of self.valoresEntrada, but you can use the type directly
typeof(self.valoresEntrada) temp = self.valoresEntrada;
[invocation setArgument:&temp atIndex:2];
Also, if you are going to store the invocation in a collection for use after the scope where it's created, you need to do [invocation retainArguments];
p.s. [[self class] instanceMethodSignatureForSelector:selector] can be written as [self methodSignatureForSelector:selector]
p.p.s. If the method signature is known and fixed at compile-time, you can use objc_msgSend directly, if you are brave.

Related

Passing value by reference to Qore script function from C++ code

I need pass returnValue to a method as argument passed by reference and adjust original var value when function id done. So using ReferenceArgumentHelper class.
What's wrong in code bellow when returnValue is unintentionally deleted (when it is a node, i.e. string) and valgrind detects it. callMethod("onFunctionExit" calls an Qore script method and I can see there correct returnValue value. I suspect it's deleted when exiting onFunctionExit when ReferenceArgumentHelper is destroyed. rah.getArg() references reference variable, so it should not be deleted in callMethod.
DLLLOCAL ThreadDebugEnum callMethod(const char* name, const ThreadDebugEnum defaultResult, QoreProgram *pgm, int paramCount, AbstractQoreNode** params, ExceptionSink* xsink) {
int rv;
QoreListNode* l = new QoreListNode();
qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
if (i == qore_program_to_object_map.end()) {
return defaultResult;
}
i->second->ref();
l->push(i->second);
for (int i=0; i<paramCount; i++) {
if (params[i])
params[i]->ref();
l->push(params[i]);
}
rv = qo->intEvalMethod(name, l, xsink);
l->deref(xsink);
return (ThreadDebugEnum) rv;
}
DLLLOCAL virtual ThreadDebugEnum onFunctionExit(QoreProgram *pgm, const StatementBlock *blockStatement, QoreValue& returnValue, ExceptionSink* xsink) {
AbstractQoreNode* params[2];
params[0] = getLocation(blockStatement);
ReferenceArgumentHelper rah(returnValue.takeNode(), xsink); // grab node from returnValue and pass to helper
params[1] = rah.getArg(); // caller owns ref
ThreadDebugEnum rv = callMethod("onFunctionExit", DBG_SB_RUN, pgm, 2, params, xsink);
AbstractQoreNode* rc = rah.getOutputValue(); // caller owns ref
returnValue.assign(rc); // takes reference
// returnValue.ref();
}
return rv;
}
When looking deeply I did not get why compiler is happy with code in /lib/ReferenceArgumentHelper.cpp:
struct lvih_intern {
LocalVar lv;
ExceptionSink* xsink;
ReferenceNode* ref;
DLLLOCAL lvih_intern(AbstractQoreNode* val, ExceptionSink* xs) : lv("ref_arg_helper", 0), xsink(xs) {
printd(5, "ReferenceArgumentHelper::ReferenceArgumentHelper() instantiating %p (val: %p type: '%s') \n", &lv, val, val ? val->getTypeName() : "n/a");
lv.instantiate(val); <--------------
VarRefNode* vr = new VarRefNode(strdup("ref_arg_helper"), VT_LOCAL);
vr->ref.id = &lv;
ref = new ReferenceNode(vr, 0, vr, 0);
}
class LocalVar {
....
DLLLOCAL void instantiate(QoreValue nval) const {
What is behind conversion AbstractQoreNode* to QoreValue in method call? I did not find an overloaded operator or so. I'm looking what exactly happens with references.
** EDIT **
To make a long story short, ReferenceArgumentHelper was buggy; it hadn't been used in years and was not up to date. The class has been fixed which should fix your issue I hope.
Thank you for pointing this out, and let me know if you have any further problems with this or the fix to the affected code.

Return a result of type error in a Method

Im working on the collision detection in my 2D Processing 2.2.1 game. Basically what I did was write a class which creates a box by defining the coordinates of its endpoints and which has a method to check if two of these boxes overlap. I did this by introducing a boolean which is set to true as soon two of these boxes overlap. Then basically implementing a get method which creates these boxes, I run into a return a result of type error. It says that the method is not returning the correct type of Box1. I dont really understand since the box which I am returning does fit the constructor. I am pretty sure it is due to the fact that the objects colliding are in an array which generates more and more objects with time, but I sadly do not know how I would have to change my Collider ( Box1) class.
here is the code im getting the error on:
//returning collider info
public Box1 getBox1() {
for (int i =frameCount/600; i >0; i--) {
return new Box1( block[i].x - Blockpic.width/2, block[i].y-Blockpic.height/2, block[i].x+Blockpic.height/2, block[i].y+Blockpic.height/2);
}
}
this is my collider (Box1) class:
public class Box1 {
float x1, x2;
float y1, y2;
Box1( float x1, float y1, float x2, float y2 ) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
boolean isOverlap( Box1 b ) {
if ((( x1 <= b.x1 && b.x1 <= x2 ) || ( x1 <= b.x2 && b.x2 <= x2 ))
&& (( y1 <= b.y1 && b.y1 <= y2 ) || ( y1 <= b.y2 && b.y2 <= y2 ))) {
return true;
}
return false;
}
}
just for complete info my spawning objects class ( where the error is situated) :
public class Blockfield {
private int Blockcount;
private PImage Blockpic;
private Block block[];
//Constructor
public Blockfield (int Blockcount) {
this.Blockcount = Blockcount;
Blockpic = loadImage("block2.png");
//new array
block = new Block [Blockcount];
for ( int i=0; i < Blockcount; i++) {
block[i] = new Block( width+Blockpic.width, random (height),7);
}
}
//Draw method for this class
public void draw () {
for (int i =frameCount/600; i >0; i--) {
pushMatrix();
translate ( block[i].x,block[i].y );
image ( Blockpic, block[i].x, block[i].y);
popMatrix();
}
}
public void update() {
for (int i =frameCount/600; i >0; i--) {
//moves blocks right to left
block[i].x -=(6 * (frameCount/200));
//spawns block when they leave the screen
if (block[i].x < 0 - Blockpic.width) {
block[i] = new Block( width+Blockpic.width, random (height),7);
}
}
}
//returning collider info
public Box1 getBox1() {
for (int i =frameCount/600; i >0; i--) {
return new Box1( block[i].x - Blockpic.width/2, block[i].y-Blockpic.height/2, block[i].x+Blockpic.height/2, block[i].y+Blockpic.height/2);
}
}
}
class Block {
float x, y;
int speed;
Block ( float x, float y, int speed) {
this.x= x;
this.y= y;
this.speed = speed;
}
}
Thanks alot!!!
The problem, as you say, is with this method:
public Box1 getBox1() {
for (int i =frameCount/600; i >0; i--) {
return new Box1( block[i].x - Blockpic.width/2, block[i].y-Blockpic.height/2, block[i].x+Blockpic.height/2, block[i].y+Blockpic.height/2);
}
}
Ignoring for a second that it doesn't make sense to have a return statement inside a for loop like this, the whole problem is that computers are too stupid to know what the value of frameCount is before they run the code. What if frameCount is 0? Or negative?
If frameCount is 0 or negative, then the body of the for loop will never be executed, and this method will never return anything. That's the error.
You might know that frameCount will always be positive, but the computer doesn't.
Edit: Continuing in response to your below comment:
If you want help, you have to provide an MCVE. Note that this should be as few lines as possible, just to get the basics across. We don't need any collision detection, just a function you call. Here's an example:
void setup(){
String s = getString(true);
println(s);
}
String getString(boolean b){
if(b){
return "testing";
}
}
If you try to run this code, you'll get an error telling you that "This method must return a result of type String".
The reason you get this error is because: what will the getString() function return if I pass in a value of false? It won't return anything! This is exactly like what your code is complaining about. We can see that getString() is only ever called with a value of true, but the computer isn't smart enough to figure that out.
You seem to misunderstand the power that a compiler has. It can't see what will happen at runtime. Even if it's obvious to you that the boolean will always be true (or in your case, that frameCount is always positive), the compiler can't know that. And since it can't know that, it's telling you that you might not return a value from a method with a return type, and that's a compiler error.
You need to refactor your code so that it always returns something from methods that have a return type. However, I'm skeptical that the for loop does what you think it does- but you haven't really explained what you think it does, so that's just a guess.
And the reason you didn't encounter this error in your other methods is because none of them contain this logical error. The only other function that has a return type is this one:
boolean isOverlap( Box1 b ) {
if (lotsOfLogic) {
return true;
}
return false;
}
Notice how even if the if statement evaluates to false, you still return something from this function. That's what you need to do with your getBox1() function.

Nested bridge transfer call with ARC

I'm trying to get the email address of a contact and the type of the email address (work/home). This is the code I've written
//Assume that 'personRef' of type ABRecordRef is available
....
ABMultiValueRef emailRef = ABRecordCopyValue(personRef, kABPersonEmailProperty);
NSMutableArray *emailAddresses = nil, *emailAddressLabels = nil;
int ctr = ABMultiValueGetCount(emailRef);
if(ctr!=0) {
emailAddresses = [[NSMutableArray alloc]init];
emailAddressLabels = [[NSMutableArray alloc]init];
for(int i=0; i<ctr; i++) {
NSString *eId = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(emailRef, i);
[emailAddresses addObject:eId];
CFStringRef label = ABMultiValueCopyLabelAtIndex (emailRef, i);
if(label!=NULL) {
NSString *eType = (__bridge_transfer NSString*)ABAddressBookCopyLocalizedLabel(label);
if([eType isEqualToString:#""]) {
[emailAddressLabels addObject:#"Email"];
} else {
[emailAddressLabels addObject:eType];
}
CFRelease(label);
}
}
}
The code crashes at CFRelease(label), but to prevent memory leak, I should be doing it. When I try the following
NSString *eType = (__bridge_transfer NSString*) ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex (emailRef, i));
I get the following warning from ARC
1. Call to function 'ABMultiValueCopyLabelAtIndex' returns a Core Foundation object with a +1 retain count
2. Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1
Now the question I have is, How to do a nesting __bridge_transfer call?
NSString *eType = (__bridge_transfer NSString*)
ABAddressBookCopyLocalizedLabel(
ABMultiValueCopyLabelAtIndex (emailRef, i) /* <-- this object is leaked */
);
This code is invalid because you leak the Label here (which I realise is your point I guess?).
You should run that code under the Instruments NSZombie instrument, it will trace all the retain/releases and you'll have a clue what's going on, because frankly, looking at the code, I don't see why it's wrong.

X++ passing current selected records in a form for your report

I am trying to make this question sound as clear as possible.
Basically, I have created a report, and it now exists as a menuitem button so that the report can run off the form.
What I would like to do, is be able to multi-select records, then when I click on my button to run my report, the current selected records are passed into the dialog form (filter screen) that appears.
I have tried to do this using the same methods as with the SaleLinesEdit form, but had no success.
If anyone could point me in the right direction I would greatly appreciate it.
Take a look at Axaptapedia passing values between forms. This should help you. You will probably have to modify your report to use a form for the dialog rather than using the base dialog methods of the report Here is a good place to start with that!
Just wanted to add this
You can use the MuliSelectionHelper class to do this very simply:
MultiSelectionHelper selection = MultiSelectionHelper::createFromCaller(_args.caller());
MyTable myTable = selection.getFirst();
while (myTable)
{
//do something
myTable = selection.getNext();
}
Here is the resolution I used for this issue;
Two methods on the report so that when fields are multi-selected on forms, the values are passed to the filter dialog;
private void setQueryRange(Common _common)
{
FormDataSource fds;
LogisticsControlTable logisticsTable;
QueryBuildDataSource qbdsLogisticsTable;
QueryBuildRange qbrLogisticsId;
str rangeLogId;
set logIdSet = new Set(Types::String);
str addRange(str _range, str _value, QueryBuildDataSource _qbds, int _fieldNum, Set _set = null)
{
str ret = _range;
QueryBuildRange qbr;
;
if(_set && _set.in(_Value))
{
return ret;
}
if(strLen(ret) + strLen(_value) + 1 > 255)
{
qbr = _qbds.addRange(_fieldNum);
qbr.value(ret);
ret = '';
}
if(ret)
{
ret += ',';
}
if(_set)
{
_set.add(_value);
}
ret += _value;
return ret;
}
;
switch(_common.TableId)
{
case tableNum(LogisticsControlTable):
qbdsLogisticsTable = element.query().dataSourceTable(tableNum(LogisticsControlTable));
qbrLogisticsId = qbdsLogisticsTable.addRange(fieldNum(LogisticsControlTable, LogisticsId));
fds = _common.dataSource();
for(logisticsTable = fds.getFirst(true) ? fds.getFirst(true) : _common;
logisticsTable;
logisticsTable = fds.getNext())
{
rangeLogId = addrange(rangeLogId, logisticsTable.LogisticsId, qbdsLogisticsTable, fieldNum(LogisticsControlTable, LogisticsId),logIdSet);
}
qbrLogisticsId.value(rangeLogId);
break;
}
}
// This set the query and gets the values passing them to the range i.e. "SO0001, SO0002, SO000£...
The second methods is as follows;
private void setQueryEnableDS()
{
Query queryLocal = element.query();
;
}
Also on the init method this is required;
public void init()
{
;
super();
if(element.args() && element.args().dataset())
{
this.setQueryRange(element.args().record());
}
}
Hope this helps in the future for anyone else who has the issue I had.

How can I replace class_createInstance in arc?

I have this code and need to port it to arc but I dont know how:
case FIELDTYPE_OBJECT:
className = [fieldType substringWithRange:NSMakeRange(2, [fieldType length]-3)];
rel = class_createInstance(NSClassFromString(className), sizeof(unsigned));
Class theClass = [rel class];
if ([rel isKindOfClass:[DbObject class]]) {
//Load the record...
NSInteger Id = [rs intForColumn:[theClass relationName]];
if (Id==0) {
fieldValue = [rel init];
} else {
Db *db = [Db currentDb];
fieldValue = [db loadById: theClass theId:Id];
}
}
break;
The error is:
error: 'class_createInstance' is unavailable: not available in automatic reference counting mode
How replace it?
I need to build class objects in runtime.
The most straightforward solution is to add another file which has -fno-objc-arc set on it, and which has a function which calls class_createInstance() as above.
Try this:
#include <objc/objc-runtime.h>
id object = [[NSClassFromString(#"TheClassName") alloc] init];
Create a separated .h/.c files and put something like this.
id const
MyCreateInstanceOfClass(Class const class)
{
id instance = class_createInstance(class, 0);
return instance;
}
#include the .h, and call it. No need to put -fno-bjc-arc switch for each file.

Resources