Added support for varargs methods! I have tested this with arrayWithObjects method, and it works OK. Last argument must be nil in objective c, and in python we need to pass None insted of nil. Later internally in pyobjus, None is convered to NULL, otherwise we are getting segmentation fault

This commit is contained in:
ivpusic 2013-06-26 12:56:17 +02:00
parent ce5e0ce772
commit 5f58c2b1ac
9 changed files with 56 additions and 14 deletions

View File

@ -2,8 +2,6 @@
#include <objc/objc-runtime.h>
#include <stdio.h>
#include <dlfcn.h>
#include <Python.h>
#include <stdint.h>
static void pyobjc_internal_init() {
static void *foundation = NULL;

View File

@ -10,7 +10,6 @@ include "common.pxi"
include "runtime.pxi"
include "ffi.pxi"
include "type_enc.pxi"
from ctypes import c_void_p
from debug import dprint
# do the initialization!
@ -111,12 +110,16 @@ cdef class ObjcMethod(object):
cdef int is_static
cdef object signature_return
cdef object signature_args
# this attribute is required for pyobjus varargs implementation
cdef object signature_default_args
cdef Class o_cls
cdef id o_instance
cdef SEL selector
cdef SEL *selectors
cdef ObjcClassInstance p_class
cdef int is_varargs
cdef object a
cdef object b
cdef int is_ready
cdef ffi_cif f_cif
cdef ffi_type* f_result_type
@ -129,6 +132,7 @@ cdef class ObjcMethod(object):
self.name = None
self.selector = NULL
self.selectors = NULL
self.is_varargs = 0
def __dealloc__(self):
self.is_ready = 0
@ -199,7 +203,6 @@ cdef class ObjcMethod(object):
self.is_ready = 1
def __get__(self, obj, objtype):
if obj is None:
return self
@ -207,11 +210,35 @@ cdef class ObjcMethod(object):
self.o_instance = oc.o_instance
return self
def __call__(self, *args):
#if self.is_static:
# return self._call_class_method(*args)
def __call__(self, *args, **kwargs):
if len(args) > (len(self.signature_args) - 2):
dprint("preparing potential varargs method...", type='i')
self.is_varargs = True
self.is_ready = False
# we are substracting 2 because first two arguments are selector and self
self.signature_default_args = self.signature_args[:]
num_of_signature_args = len(self.signature_args) - 2
num_of_passed_args = len(args)
num_of_arguments_to_add = num_of_passed_args - num_of_signature_args
for i in range(num_of_arguments_to_add):
self.signature_args.append(self.signature_args[-1])
# we need prepare new number of arguments for ffi_call
self.ensure_method()
return self._call_instance_method(*args)
def _reset_method_attributes(self):
'''Method for setting adapted attributes values to default ones
'''
dprint("reseting method attributes...", type='i')
self.signature_args = self.signature_default_args
self.is_ready = False
self.ensure_method()
# this is little optimisation in case of calling varargs method multiple times with None as argument
self.is_varargs = False
def _call_instance_method(self, *args):
dprint('-' * 80)
@ -220,14 +247,12 @@ cdef class ObjcMethod(object):
dprint('--> want to call', self.name, args)
dprint('--> return def is', self.signature_return)
dprint('--> args def is', self.signature_args)
cdef ffi_arg f_result
cdef void **f_args
cdef int index
cdef size_t size
cdef ObjcClassInstance arg_objcclass
# allocate f_args
f_args = <void**>malloc(sizeof(void *) * len(self.signature_args))
if f_args == NULL:
@ -254,7 +279,7 @@ cdef class ObjcMethod(object):
for index in range(2, len(self.signature_args)):
# argument passed to call
arg = args[index-2]
# we already know the ffitype/size being used
val_ptr = <void*>malloc(self.f_arg_types[index][0].size)
dprint("index {}: allocating {} bytes for arg: {!r}".format(
@ -275,8 +300,11 @@ cdef class ObjcMethod(object):
(<char **>val_ptr)[0] = <char *><bytes>arg
elif sig == '@':
dprint('====> ARG', <ObjcClassInstance>arg)
ocl = <ObjcClassInstance>arg
(<id*>val_ptr)[0] = <id>ocl.o_instance
if arg == None:
(<id*>val_ptr)[0] = <id>NULL
else:
ocl = <ObjcClassInstance>arg
(<id*>val_ptr)[0] = <id>ocl.o_instance
# method is accepting class
elif sig == '#':
dprint('===> Class arg', <ObjcClassInstance>arg)
@ -296,11 +324,14 @@ cdef class ObjcMethod(object):
f_args[f_index] = val_ptr
dprint('pointer before ffi_call:', pr(f_args[f_index]))
ffi_call(&self.f_cif, <void(*)()>objc_msgSend, &f_result, f_args)
sig = self.signature_return[0]
dprint("return signature", sig, type="i")
if self.is_varargs:
self._reset_method_attributes()
cdef id ret_id
cdef ObjcClassInstance cret
cdef bytes bret

View File

@ -53,3 +53,16 @@ print array.retainCount()
array.release()
print array.retainCount()
array_test = NSArray.arrayWithObjects_(text, newText, text, array, None)
print array_test.count()
array_new = NSArray.arrayWithObjects_(text, newText, None)
array_new = NSArray.arrayWithObjects_(None)
print array_new.count()
array_new = NSArray.arrayWithObjects_(None)
array_new = NSArray.arrayWithObjects_(text, None)
array_new = NSArray.alloc().initWithObjects_(None)
array_new = NSArray.alloc().initWithObjects_(text, None)
print array_new.objectAtIndex_(0).UTF8String()
array_new = NSArray.alloc().initWithObjects_(None)
print array_new.count()