【问题标题】:iOS Design: Using the delegate pattern in a libraryiOS 设计:在库中使用委托模式
【发布时间】:2010-10-18 19:14:39
【问题描述】:

我有一个库项目,它使用 ASIHTTPRequest 发出 URL 请求并解析响应。该库将由一个单独的 iPhone 应用程序项目使用。

如果我的 iPhone 控制器代码响应触摸事件,然后调用库来发出 URL 请求,我如何最好地异步执行请求?

在库中,如果我将委托模式用于异步请求,如 ASIHTTPRequest 示例代码所示,我如何将库中的数据返回给 iPhone 控制器中的调用代码?

如果我改为在库中使用 ASIHTTPRequest 发出同步 URL 请求,那么将 iPhone 控制器对库的调用放在单独的线程上以避免占用 UI 线程的最简单方法是什么?

【问题讨论】:

    标签: iphone design-patterns delegates ios


    【解决方案1】:

    我不是 ASIHTTPRequest 专家(NSURLRequest 一直做得很好),但是从快速查看代码来看,您似乎可以使用它的委托和 didFinishSelector 属性来告诉它何时 URL 请求是完成的。所以,例如:

    - (void)startURLRequest
    {
        ASIHTTPRequest *myRequest;
    
        /* code to set the request up with your target URL, etc here */
    
        myRequest.delegate = self;
        myRequest.didFinishSelector = @selector(HTTPRequestDidFinish:);
    
        /* ... */
    
        [myRequest startAsynchronous];
    }
    
    - (void)HTTPRequestDidFinish:(ASIHTTPRequest *)request
    {
        NSLog(@"Request %@ did finish, got data: %@", request, request.data);
        [myTargetForData didReceiveData:request.data fromURL:request.originalURL];
    }
    

    Apple 明确建议您使用内置的 runloop 样式机制进行异步 HTTP 获取,而不是单独的线程。使用单独的线程可能会导致性能下降——至少在电池寿命和/或设备发热方面,即使它仍然足够快。

    也就是说,作为一个学习点,到目前为止,将某些东西切换到单独的线程并将其报告回主线程(请记住:UIKit 对象可能仅从主线程发送消息)的最快方法是通过更改此:

    - (void)postResult:(NSString *)result
    {
        instanceOfUILabel.text = result;
    }
    
    - (void)doExpensiveOperationOn:(NSString *)source
    {
         /* lots of expensive processing here, and then... */
         [self postResult:result];
    }
    
    - (IBAction)userWantsOperationDone:(id)sender
    {
         [self doExpensiveOperationOn:@"some value or another"];
    }
    

    进入这个:

    - (void)postResult:(NSString *)result
    {
        instanceOfUILabel.text = result;
    }
    
    - (void)doExpensiveOperationOn:(NSString *)source
    {
         /* we're on a thread without an autorelease pool now, probably we'll want one */
         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
         /* lots of expensive processing here, and then... */
    
         /* in this simplified example, we assume that ownership of 'result' is here on this thread, possibly on the autorelease pool, so wait until postResult has definitely finished before doing anything that might release result */
         [self performSelectorOnMainThread:@selector(postResult:) withObject:result waitUntilDone:YES];
    
         [pool release];
    }
    
    - (IBAction)userWantsOperationDone:(id)sender
    {
         [self performSelectorOnBackgroundThread:@selector(doExpensiveOperationOn:) withObject:@"some value or another"];
    }
    

    尽管不考虑线程,但您可能会产生大约一百万个并发错误,在该示例中,一个明显的问题是触发 IBAction 的任何内容都可能 [可能] 在 doExpensiveOperationOn 完成之前再触发几次。多线程不是轻而易举的事情。

    【讨论】:

    • 谢谢,我知道 ASIHTTPReuquest 中的代表,但是将信息从它们传递回调用代码的最佳方法是什么?在您的示例中, ASIHTTPRequest 的代码位于库中。对象 myTargetForData 是库外的调用对象。所以为了调用它的方法,我必须传入对调用对象的引用。这真的是最干净的方法吗?委托模式的目的是什么?
    • 我当时显然给出了一个完全没有帮助的答案!就我而言,我通常发现我发布 HTTP 请求的类知道将什么逻辑应用于结果以获得我可以在运行时使用的东西,所以我倾向于将结果处理到我正在使用的任何类中封装结果,然后将其传递给下一个委托。因此,如果我有一个 JSON 或 XML 解析,那么它会连接到一个单一的类,该类是 NSURLRequest (或在你的情况下为 ASIHTTPRequest )的委托,然后结果返回到我的实际模型类。除此之外的细节取决于任务。
    【解决方案2】:

    对于任何人的未来参考,我发现的最简单的方法是使用 ASIHTTPRequest 中内置的异步请求功能,将我的库对象设置为委托,并将 didFinishSelector: 和 didFailSelector: 值设置为我库中每个请求的不同方法。

    在处理每个响应结束时,我将解析的响应(NSString* 或 NSArray*)分配给我的库对象的属性,而不是返回值。

    当我的 iOS 视图控制器委托被加载时,我使用 Key-Value Observing 为库中的每个属性添加一个更改观察者。当响应被解析并分配给库中的属性时,在我的视图控制器委托的代码中调用了 observeValueForKeyPath:ofObject:change:context: 方法,从那里我可以找出哪个属性被更改了,因此什么 UI需要更新。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      相关资源
      最近更新 更多