Forum Archive

ObjC: Scheduling a stream on a run loop

mikael

I have a stream that shows status open, and accepts bytes that I write to it, but the delegate handler callback is not firing. I suspect I might not be scheduling the stream on the run loop properly:

  stream.scheduleInRunLoop_forMode_(NSRunLoop.mainRunLoop(), 0)

I am especially unsure about the forMode parameter, which is supposed to be NSDefaultRunLoopMode global varuable. What is the right value to use in Python?

dgelessus

NSDefaultRunLoopMode is a global variable of type NSRunLoopMode, which is a typedef for NSString *. To get the value of that variable, you can do the following:

NSDefaultRunLoopMode = ObjCInstance(c_void_p.in_dll(c, "NSDefaultRunLoopMode"))

Then you can pass that object in the forMode part of the method call. 0 doesn't work because it's interpreted as a NULL pointer, which probably isn't a valid argument here.

mikael

@dgelessus, ”extra credit” for leading me to the ctypes documentation and equipping me to maybe solve the next similar case myself.

mikael

Ah, seems I cannot do any objc stuff on my own...

This time the issue is that NSInputStream’s read:maxLength: needs a pointer to a buffer for storing the data coming from the stream. I have played with various ideas, including the following:

buffer = bytearray(1024)
buf_p = ctypes.c_void_p.from_buffer(buffer)
read_len = stream.read_maxLength_(buf_p, 1024)

... but all I get is a crash on read. Any pointers? Thanks!

dgelessus

@mikael The from_buffer method doesn't do what you think it's doing. ctypes.c_void_p.from_buffer(buffer) assumes that buffer contains data for a c_void_p, and gives you a c_void_p backed by that data. Basically, it's not giving you a pointer to buffer, it's interpreting the start of buffer as a pointer. Since buffer starts out as all zeroes, buf_p is a null pointer, so writing to it will crash.

I'm not quite sure, but you might be able to just pass buffer directly into the method. ctypes might convert it to a pointer automatically - it does that for (read-only) bytes objects, I'm not sure if it works with bytearrays as well.

If not, you can use ctypes.create_string_buffer to create a new c_char array of the given length. This array can definitely be passed into C functions/methods. To get the data out of the array (after you've read into it), you can use the raw attribute (to get the entire data) or the value attribute (to get everything up to the first zero byte - this is useful for C strings).

mikael

@dgelessus, thanks! Plain bytearray did not work, but create_string_array did, and I was able to create a bytearray out of its contents with the number of bytes read that read:maxLength: returns.