5

我正在用 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 返回其值之后)。

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

我没有提到的事情:我的 ObjC_Class 有一个名为“object”的元素,它的类型为“id”,它存储了对 Python 对象所代表的 Objective-C 对象的引用......

再补充一点:我正在链接 Python 2.6(.3?),它主要是静态链接的

更新 我已经设法通过将 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

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

4

1 回答 1

6

嗯,这很尴尬......我发现了这个问题(以防其他人遇到同样的问题)。我通过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;
}

所以我想,“也许我的 PyMethodDef 方法在 Python 看到它时超出了范围(或其他东西)”(如果 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;
    }
}

现在,请原谅我,我要去学习“静态”。

于 2013-01-29T18:45:11.293 回答