【问题标题】:Call back style回调样式
【发布时间】:2009-05-04 17:59:38
【问题描述】:

我正在编写一个 iPhone 应用程序,它在许多地方都需要执行非常简单的请求响应类型的非 HTTP 或 FTP 网络。

我已将所有这些封装到一个与运行循环集成的 SimpleQuery 类中。

SimpleQuery *lookup = [[SimpleQuery alloc] init];
[lookup setDelegate:self];
[lookup doQueryToHost:queryServer port:queryPort query:queryString ];   

如您所见,调用对象将自己设置为委托。结果完成后,它会使用结果调用委托上的方法。

[delegate simpleQueryResult:resultString simpleQuery:self];             

我现在有一个 SimpleQuery 用户,它有两种类型的查询,所以我需要扩展 SimpleQuery 来支持它。

我能想到两种明智的做法。

首先将一个选择器传递给doQueryString,或者一个单独的doQueryStringWithSelector。

[lookup doQueryToHost:queryServer port:queryPort query:queryString selector:@SEL ]; 

第二次将标签传递给doQueryString,这样当委托被调用时,它可以在传递simpleQuery时查询标签,以找出结果的用途。

[lookup doQueryToHost:queryServer port:queryPort query:queryString withTag:tag ];

我只是想知道从编码风格的角度来看哪个最好,第一个似乎更简单,但标记似乎更符合 iPhone SDK 和 Interface Builder

【问题讨论】:

    标签: iphone objective-c coding-style


    【解决方案1】:

    Apple 的代码(例如,在 UIControl 中)中常用的一个选项是提供目标对象和选择器。这仅在存在单个回调时才有效,并且在这种情况下比委托更合适。 (如果有多个回调,那么您可能必须使用委托和标记方法。)

    如果你走这条路,那么你完全取消了委托,而是有一个带有这样签名的方法:

    doQueryToHost:(id)queryServer port:(int)queryPort query:(NSString*)queryString target:(id)target action:(SEL)action
    

    请注意,在这种情况下,在方法参数中,“action”通常优于“selector”。完成后,查询将简单地调用目标上的选择器。这将允许您的客户拥有多个选择器,以及多个目标对象;这有助于清理代码,因为您不需要将所有内容都塞入单个委托对象中。

    如果你想使用你的标签路由,你应该称之为“context”,这是 Apple 使用的(例如,在 addObserver:forKeyPath:options:context 中)。

    【讨论】:

      【解决方案2】:

      第三个选项是套件中的常见模式,即使用@protocols。

      例如:

      @protocol QueryCompleteHandlerProtocol
      - (void)queryType1Complete:(int)intStuff;
      - (void)queryType2Complete:(float)floatStuff;
      @end
      

      它的作用是声明采用该协议的对象必须遵守的一组方法调用(编译器实际上会强制执行此操作)。

      因此,您的 SimpleQuery 对象将保留委托指针之类的东西,您可以在 ivars 中这样声明:

      NSObject<QueryCompleteHandlerProtocol> *callback;
      

      这告诉编译器callback 是一个从NSObject 继承并采用QueryCompleteHandlerProtocol 协议的对象。有时你会看到这样写:

      id<QueryCompleteHandlerProtocol> callback;
      

      当您想调用回调时,它们没有什么特别之处,SimpleQuery 的方法只会调用:

      [callback queryType1Complete:1];
      [callback queryType2Complete:2.0];
      

      最后,您的协议类客户端将声明自己采用该协议:

      @interface MyClass : NSObject<QueryCompleteHandlerProtocol>
      ...
      @end
      

      并将自己设置为回调,代码如下:

      [lookup setCallback:self];
      

      这是编译器检查MyClass 是否符合QueryCompleteHandlerProtocol 的地方,这意味着它已经实现了queryType1Complete:queryType2Complete:

      【讨论】:

      • -(void)simpleQuery:(SimpleQuery *)sq completeWithValue:(NSValue *)value;更像是苹果设计委托协议的惯例。还可以在 SimpleQuery 中添加 -tag/-setTag: 方法来区分它们
      • 很遗憾,协议不起作用,因为 SimplyleQuery 对象不知道查询的目的。所以它无法决定使用哪个协议。应该更清楚我现在已经在使用单个回调定义的协议。
      • 听起来您将一些代码放在 SimpleQuery 类中只是为了将它们放在一个地方。如果您还允许该类稍微抽象一下功能并让它知道它正在做什么的语义(例如,知道查询的目的),您最终可能会在调用代码中减少通用性,这反过来会使 SimpleQuery API 更简单.
      【解决方案3】:

      我不确定我是否理解这里的问题。 SimpleQuery 的用户不能为第二个查询设置另一个委托对象,或者在 simpleQuery: 参数上进行分支吗?这是委托模式的基本部分,就像一个视图控制器有两个 UIActionSheets。

      【讨论】:

      • 我应该明确一点,SimpleQuery 实例只有一个委托。这个问题是一种委托使用 SimpleQuery 进行两种不同类型的查询,因此当通过 simpleQueryResult 函数返回结果时,委托需要知道结果的用途。
      • 我认为 Brent 的意思是您可以为第二个查询创建第二个 SimpleQuery 对象,然后为其设置另一个委托,或者在 simpleQuery: 参数上分支。
      猜你喜欢
      • 2011-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多