微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Objective-C中的Python-C Api包装器在传递Python对象时会调用__getattr__而崩溃

我正在使用Objective-C编写一个轻量级接口,它能够执行python脚本并在Objective-C和Python之间来回传递数据.我已经研究过PyObjC和ObjP,而且我都没有找到它(因为我正在为iOS开发< = 6.0.1 PyObjC不会编译为大量使用NSMapTable). 所以我基本上在Objective-C中创建了一个名为“ObjC_Class”的Python类型(创意,没有?),我希望这个Python对象几乎类似于ObjC对象.所以我决定覆盖类的__getattr__函数,这样我就可以访问该类的ObjC等价物的任意方法属性. 这是代码

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    Nsstring *attrName = [Nsstring stringWithCString:PyString_Asstring(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        //PyObject* (*fpFunc)(PyObject*,PyObject*) = ObjC_Class_msg_send;
        PyMethodDef methd = {[attrName UTF8String],ObjC_Class_msg_send,METH_VaraRGS,[attrName UTF8String]};
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

现在,当我说:

示例#1

from ObjC import ObjC_Class
new = ObjC_Class('UIView')
new.backgroundColor("asdf", 42, "some_random_string") # This does not crash

但是当我跑步时:

例#2

from ObjC import ObjC_Class
new = ObjC_Class('UIView')
moreStuff = "some_random_string" # or "42" or [1,2,3] or anything else...
new.backgroundColor("asdf", 42, moreStuff) # !!! This does crash

它崩溃说:

error: address doesn't contain a section that points to a section in a object file

当我尝试调用不存在的函数时,我已经看到了这个错误,但我无法想象为什么第一个会起作用,但第二个不会.

这是ObjC_Class_msg_send函数的实现:

static PyObject* ObjC_Class_msg_send(PyObject *self, PyObject *args)
{
    NSLog(@"Entering...");
    NSMutableArray *tmp = [[NSMutableArray alloc] init];
    for(int i=0;i<PyTuple_Size(args);i++)
    {
        [tmp addobject:Py_to_ObjC(PyTuple_GetItem(args, i))];
    }
    NSLog(@"Object: %@, Method Name: %@, args: %@", ((ObjC_Class*)self)->object, methodName, tmp);
    methodName = @"";
    return PyString_FromString("Did it actually work!?!?!");
}

当我运行将python变量传递给函数的示例时,它甚至在调用ObjC_Class_msg_send之前崩溃(但在ObjC_Class_getattro返回其值之后).

(哦,请原谅邋code的代码……我正在努力在为这个项目分配太多时间之前先运行一个简单的概念验证)

我没有提到的东西:我的ObjC_Class有一个名为’object’的元素,它的类型为’id’,它存储了对象所代表的Objective-C对象的引用…

还有一个注意事项:我正在链接Python 2.6(.3?),它主要是静态链接

UPDATE
通过将fpFunc定义为静态删除fpFunc,我已经设法让它停止崩溃…我甚至不确定为什么我在那里有它(愚蠢的复制粘贴……):

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    Nsstring *attrName = [Nsstring stringWithCString:PyString_Asstring(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        //static PyObject* (*fpFunc)(PyObject*,PyObject*) = ObjC_Class_msg_send; // static Now
        PyMethodDef methd = {[attrName UTF8String],ObjC_Class_msg_send,METH_VaraRGS,[attrName UTF8String]};
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

但是……现在Python正在抛出错误(当我将python变量作为参数传递时,即:上面的示例#2):

SystemError: Objects/methodobject.c:120: bad argument to internal function

:(我以前从未见过这个……

解决方法:

这是令人尴尬的…我发现了问题(如果其他人有同样的问题).我通过python源跟踪错误,发现错误在这里引发的:

PyObject *
PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw)
{
    PyCFunctionObject* f = (PyCFunctionObject*)func;
    PyCFunction meth = PyCFunction_GET_FUNCTION(func);
    PyObject *self = PyCFunction_GET_SELF(func);
    Py_ssize_t size;

    switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) {
    case METH_VaraRGS:
        if (kw == NULL || PyDict_Size(kw) == 0)
            return (*meth)(self, arg);
        break;
    case METH_VaraRGS | METH_KEYWORDS:
    case METH_OLDARGS | METH_KEYWORDS:
        return (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
    case METH_NOARGS:
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 0)
                return (*meth)(self, NULL);
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes no arguments (%zd given)",
                f->m_ml->ml_name, size);
            return NULL;
        }
        break;
    case METH_O:
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 1)
                return (*meth)(self, PyTuple_GET_ITEM(arg, 0));
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes exactly one argument (%zd given)",
                f->m_ml->ml_name, size);
            return NULL;
        }
        break;
    case METH_OLDARGS:
        /* the really old style */
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 1)
                arg = PyTuple_GET_ITEM(arg, 0);
            else if (size == 0)
                arg = NULL;
            return (*meth)(self, arg);
        }
        break;
    default:
        PyErr_BadInternalCall();
        return NULL;
    }
    PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
             f->m_ml->ml_name);
    return NULL;
}

所以我想,“当Python看到它时,也许我的PyMethodDef方法超出了范围(或其他东西)”(如果Python对包含变量的参数的函数进行不同的处理,而不是在具有const参数的函数上执行导致前者在管道的某处延迟…).

所以我把这个函数拉出来并宣布它是静态的(这听起来很熟悉……)瞧!它工作得很漂亮.这是更新的代码

static PyMethodDef methd = {"blah",ObjC_Class_msg_send,METH_VaraRGS,"blech"};

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    Nsstring *attrName = [Nsstring stringWithCString:PyString_Asstring(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

现在,如果你能原谅我,我将继续学习’静态’.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐