Skip to content

Conversation

@olliwang
Copy link
Contributor

The delegate support is achieved by a objc_delegate() function that converts a Python object to the corresponded Objective C delegate instance. A list of protocols intended to conform are also passed to the objc_delegate() function.

The objc_delegate() dynamically creates a new Objective C class inherited by NSObject, then adds desired protocols methods and instantiates the instance. The instance is converted to Python object before returned so it can be passed to other MetaObjcClass methods that requires delegate instance.

In order to achieve dynamic implementation of protocol methods. A delegate_register dict is added to remember the class name and corresponded Python object. So the dynamic function can dispatch the call to real Python object's method. However, currently registered delegates won't get released. Still looking for a way to free unused objects.

This commit also adds an example at exampls/delegate.py. However, because the protocol method will be called asynchronously. I needs to write the example as a simple Kivy app so the execution won't quit before the protocol instance gets called. There may be a simpler way to implement a event loop but I failed.

There is another problem about dynamically creating an Objective C class. That is, we need to use the objc_getProtocol() runtime function to find interested protocol methods (for signature and types). However, it seems not all protocols can be found through this function. More information about this issue can be found here: http://goo.gl/zA116F

Signed-off-by: Olli Wang olliwang@ollix.com

The delegate support is achieved by a objc_delegate() function that converts a Python object to the corresponded Objective C delegate instance. A list of protocols intended to conform are also passed to the objc_delegate() function.

The objc_delegate() dynamically creates a new Objective C class inherited by NSObject, then adds desired protocols methods and instantiates the instance. The instance is converted to Python object before returned so it can be passed to other MetaObjcClass methods that requires delegate instance.

In order to achieve dynamic implementation of protocol methods. A `delegate_register` dict is added to remember the class name and corresponded Python object. So the dynamic function can dispatch the call to real Python object's method. However, currently registered delegates won't get released. Still looking for a way to free unused objects.

This commit also adds an example at exampls/delegate.py. However, because the protocol method will be called asynchronously. I needs to write the example as a simple Kivy app so the execution won't quit before the protocol instance gets called. There may be a simpler way to implement a event loop but I failed.

There is another problem about dynamically creating an Objective C class. That is, we need to use the objc_getProtocol() runtime function to find interested protocol methods (for signature and types). However, it seems not all protocols can be found through this function. More information about this issue can be found here: http://goo.gl/zA116F

Signed-off-by: Olli Wang <olliwang@ollix.com>
@tito
Copy link
Member

tito commented Feb 1, 2014

Hi,

We think that the syntax you're using might be too difficult to use, and can be better. Let's decide which one would be the best: https://gist.github.com/tito/8753609

I personnally prefer the solution 4, as it will not conflict with possible similar method, and it's kind-of explicit about the protocol used in the implementation.

What do you think ?

@tito
Copy link
Member

tito commented Feb 1, 2014

By the way, the solution 3 can be easilly implemented with:

class ObjcDelegate(object):

    def __new__(cls, *args):
        instance = super(ObjcDelegate, cls).__new__(cls)
        return objc_delegate(instance, cls.__protocols__)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 1, 2014

The third solution looks pretty good. I've considered something like this but it makes code a bit complicated when figuring out how to implement delegate support from scratch. As the implementation has done. It's nice to adopt this syntax. :)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 3, 2014

Has the decision been made? Should I implement the solution or you will take care of it?

@tshirtman
Copy link
Member

I'm in favor of 4 too, explicit being better than implicit, i though more people would add their opinion, maybe give it a few days? :) Anyway, it's great already that you made it work, making the syntax better is obviously the second step :).

If you are willing, you can implement the solution you see fit, if not, it's likely it'll be done soon anyway :)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 3, 2014

I see. I was just wondering if I have to implement the solution. If you will, that would be pretty awesome. :)

@tito
Copy link
Member

tito commented Feb 3, 2014

Tell us if you do :)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 3, 2014

I think solution 3 is more conform to the Objective C style. Declares protocols once, and simply adds implementation for protocol methods. Usually methods for the same protocol are put together with a comment says the following methods are implementation for some protocol. I'm okay with solution 4. But it would be a bit tedious if you implement 10 methods for the same protocol.

@tito
Copy link
Member

tito commented Feb 3, 2014

But the 3 have a drawback: conflict with similar method name. Plus, using __ is less pythonic than decorator. Decorator are clear about what is going to be extended :) Plus, potentially, you could pass any class as delegate, as long as some method are decorated with @protocol. And that's a big plus.

Example in your context:

class MyApp(object):

    @protocol('NSURLConnectionDelegate')
    def connection_didFailWithError_(self, connection, error):
        print("Protocol method got called!!", connection, error)

    def request_connection(self):
        url = NSURL.URLWithString_(objc_str('abc'))
        request = NSURLRequest.requestWithURL_(url)
        connection = NSURLConnection.connectionWithRequest_delegate_(request, self)

I really love this decorator :)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 3, 2014

That makes sense. I thought it's better than 3 now. :)

@tito
Copy link
Member

tito commented Feb 3, 2014

Did you started the work to support it? If not, i'm gonna do it :)

@olliwang
Copy link
Contributor Author

olliwang commented Feb 3, 2014

Nope, go ahead. ;)

@tito tito merged commit dc51dfe into kivy:master Feb 3, 2014
@tito
Copy link
Member

tito commented Feb 3, 2014

Done! The example has been updated, and i'm testing with Bluetooth Low Energy right now. Thanks a lot for your contribution! Doc is now required :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants