On Jan 20, 2010, at 1:40 PM, Steven Degutis wrote:

> Recently I had the same issue you were having, sort of. And I came up with a 
> solution I really liked.
> 
> When I was playing with Distributed Objects, I fell in love with the abstract 
> simplicity. However, it blocks and that's bad. It's even worse when the 
> server stops responding, because you could potentially have a 60 second 
> timeout before the single method will return. It's a potential disaster.
> 
> So, I wrote an elegant compromise. Code is still written inline, no callbacks 
> or delegate messages needed. But, it requires Blocks (and thus 10.6) to work.

Steven - this is a really interesting approach.  I can see how this basically 
achieves the same thing as all the callback methods, but does allow the code to 
be written somewhat inline.  Unfortunately I need to at least support OS X 10.5 
at this point.  I definitely need to read-up on blocks b/c I can see how they 
can be used to work around some tricky design problems.

> 
> Essentially, I wrote some code on top of AsyncSocket (which is a brilliant 
> framework by the way) that allows me to wrap up ObjC messages as NSData, send 
> it across the server, and unpack it on the other side. The other side then 
> responds to the ObjC message as if it was called right inside the 
> application. (All this is thanks to NSInvocation's ability to introspect an 
> ObjC message, by the way).
> 
> The problem came when I had to return values. As long as the return value was 
> void, this worked like a charm. But once I wanted to return an array of 
> strings or a number, I had to define a method in the sender's protocol to 
> receive such information. This is akin to your "thousands of delegate 
> messages" you would have to implement, as you stated.
> 
> So, using Blocks and NSInvocation and AsyncSocket, I ended up writing code 
> that allows me to write code like this:
> 
> 
> // protocol.h
> 
> @protocol ServerProtocol
> 
> - (NSNumber*) calculatePiAndKillTime:(NSNumber*)shouldKillTime;
> 
> @end
> 
> 
> // client.m
> 
> - (void) someMethod {
>         id <ServerProtocol> server;
>         NSNumber *sure = [NSNumber numberWithBool:YES];
>         [[server calculatePiAndKillTime: sure]
>          returnedValue:^(id value) {
>                 // this will be called later on at some point
>                 NSLog(@"pi = %@", value);
>         }]
> }
> 
> 
> // server.m
> 
> - (NSNumber*) calculatePiAndKillTime:(NSNumber*)shouldKillTime {
>         if ([shouldKillTime boolValue])
>                 // synchronously watch some film
>                 [self goWatchTheNewStarTrekFilmFrom2009];
>         
>         return [NSNumber numberWithFloat: 3.14];
> }
> 
> 
> 
> All methods sent to a destination's proxy are sent asynchronously. And, as 
> you can see, the return value of the method -calculatePiAndKillTime: is not 
> actually an NSNumber, but rather a proxy that waits for a response from the 
> destination. When the destination responds to the source with a return value, 
> the method -returnedValue: is called with the value.
> 
> But that's only half of the coolness.
> 
> The other half is that methods can simply return the value they want right 
> inside the method, no hacks necessary or anything by the programmer. In this 
> case, we just use this line of code: return [NSNumber numberWithFloat: 3.14]; 
> and then the NSNumber object is packaged up and sent back to the source 
> through the proxy, all automagically.
> 
> The main downfall of this is that every argument and return value must be an 
> ObjC type, no scalars or structs or anything else will work with this system. 
> (Mike Ash explains pretty well on this blog why trying to support those 
> things can lead to some unfixable trickiness, which I just wanted to avoid 
> altogether.)
> 
> If you can't support 10.6, then, this won't work. But hopefully you can soon 
> ;)
> 
> Good luck.
> 
> -Steven
> 
> 
> 
> On Wed, Jan 20, 2010 at 11:39 AM, Carter R. Harrison <carterharri...@mac.com> 
> wrote:
> I need some folks experienced with cocoa and socket programming to weigh in 
> for me on some design problems I've been having.  I'm designing an 
> application that acts as a client in a client-server model.  The client 
> communicates with the server over the network by issuing a request and then 
> receiving a response.  Requests can only be issued one at a time, meaning 
> that a request cannot be sent until a response from any outstanding request 
> is first received.  My application works in such a way that the it could 
> request a handle to an object on the server and then use that handle in 
> subsequent requests to retrieve additional information about the object.  I 
> see two ways of modeling the application - I've tried both and I'm not 
> particularly happy with either.
> 
> The first is to send a request, and then have the socket block until a 
> response is received.  This benefit to this model is that it is so much 
> easier to write the higher level application code.  The issue with this model 
> is that over a slow network connection it can take a considerable amount of 
> time for the response to come back from the server and while that is 
> happening my CPU usage is through the roof b/c the thread is blocking.
> 
> The second way is to send a request and then let the NSInputStream call a 
> delegate method when the response data is available.  The response data is 
> then pushed up through my protocol stack and finally up to the higher level 
> application code.  The benefit to this method is that CPU usage is minimal 
> due to the fact that I'm no longer blocking, but the downside is that the 
> higher level application code is so much more difficult to write because I 
> have to write about a thousand methods to act as a callback for each request 
> in a series of requests.
> 
> I've provided an example of how I see each working below.  My first question 
> is, is there other ways to design an application around this client-server 
> model that I'm not thinking about?  My 2nd question is, if there aren't other 
> ways, how can I adapt either method that I have outlined to make it work a 
> little bit better?
> 
> As an example let's say the server knows about the following objects:
> 
> 1. VendingMachine
>        - An object that represents a vending machine.
>        - A vending machine contains Soft Drink objects.
> 2. SoftDrink
>        - Has the following properties: drink name, price, number of calories.
> 
> If I use the blocking model, I could write my code like this.  The code is 
> simple to write but I'm forced to wait for the server to respond with 
> information on pretty much every line of code.  If the vending machine had 
> enough soft drinks it could take a long time to iterate over each one and 
> have the server respond with the drink's name of each drink.
> 
> -(void)printDrinkNames
> {
>        VendingMachine *machine = [server fetchVendingMachine];
>        NSArray *softDrinks = [machine getSoftDrinks];
>        for (int i = 0 ; i < softDrinks.count ; i++)
>        {
>                NSString *drinkName = [[softDrinks objectAtIndex:i] name];
>                NSLog(@"Found a drink named %@", drinkName);
>        }
> }
> 
> Likewise if I do the non-blocking approach I would have to have a method that 
> gets called for each step in the process (see below).  This model drives me 
> crazy b/c the higher level application code is long, has tons of methods, and 
> is just difficult to read and maintain.  The example I have provided is 
> simple enough to get the point across, but in reality some of the processes 
> I'm trying to drive are much more complex and require numerous callback 
> methods to pull off.
> 
> -(void)printDrinkNames
> {
>        [server fetchVendingMachineWithCallBackObject:self 
> selector:@selector(didFetchVendingMachine:)
> }
> 
> -(void)didFetchVendingMachine:(VendingMachine *)machine
> {
>        [machine fetchSoftDrinksWithCallBackObject:self 
> selector:@selector(didFetchSoftDrinks:)];
> }
> 
> -(void)didFetchSoftDrinks:(NSArray *)drinks
> {
>        for (int i = 0 ; i < drinks.count ; i++)
>        {
>                SoftDrink *drink = [drinks objectAtIndex:i];
>                [drink fetchNameWithCallBackObject:self 
> selector:@selector(didFetchDrinkName:)]
>        }
> }
> 
> -(void)didFetchDrinkName:(NSString *)name
> {
>        NSLog(@"Drink name is %@", name);
> }
> 
> _______________________________________________
> 
> Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)
> 
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
> 
> Help/Unsubscribe/Update your Subscription:
> http://lists.apple.com/mailman/options/cocoa-dev/steven.degutis%40gmail.com
> 
> This email sent to steven.degu...@gmail.com
> 
> 
> 
> -- 
> Steven Degutis
> http://www.thoughtfultree.com/
> http://www.degutis.org/

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to