目次
高度な iOS ネットワーク プログラミング: iPhone および iPad エンタープライズ アプリケーション開発のためのエラー処理
5.1 エラーソースを理解する
5.1.1 オペレーティングシステムエラー
ホームページ バックエンド開発 PHPチュートリアル 高度な iOS ネットワーク プログラミング: iPhone および iPad エンタープライズ アプリケーション開発のエラー処理_PHP チュートリアル

高度な iOS ネットワーク プログラミング: iPhone および iPad エンタープライズ アプリケーション開発のエラー処理_PHP チュートリアル

Jul 13, 2016 am 10:22 AM
アプリケーション開発 通信網 間違い

高度な iOS ネットワーク プログラミング: iPhone および iPad エンタープライズ アプリケーション開発のためのエラー処理

リーリー

この章の内容

● iOS アプリのネットワーク エラーの原因

● ネットワークの到達可能性を確認する

● エラー処理の経験則

● ネットワークエラーを処理するための設計パターン

これまで、私たちが紹介した iPhone と他のシステム間のネットワーク相互作用は、すべてが正常であるという前提に基づいています。この章では、この前提を捨てて、ネットワークの現実の世界を掘り下げていきます。現実の世界では、電話がネットワークに出入りしたり、ネットワーク インフラストラクチャに問題が発生したり、時にはユーザーが間違いを犯したりするなど、事態は最悪の事態に陥ります。すべてがうまくいけば、iOS アプリの作成はずっと簡単になりますが、残念ながらそうではありません。この章では、ネットワーク操作の障害を引き起こすいくつかの要因を検討し、システムがアプリケーションに障害を通知する方法と、アプリケーションがユーザーに適切に通知する方法を紹介します。さらに、この章では、アプリケーション ロジックにエラー処理コードを追加せずに、クリーンかつ一貫した方法でエラーを処理するためのソフトウェア パターンを紹介します。

5.1 エラーソースを理解する

初期の iOS には素晴らしい天気予報アプリがありました。 Wi-Fi や良好な携帯電話ネットワークでは正常に動作しますが、ネットワークの品質がそれほど良くない場合、天気予報アプリが風邪のようにホーム画面でクラッシュします。ネットワーク エラーが発生するとパフォーマンスが低下するアプリケーションが数多くあり、大量の UIAlertView がポップアップ表示され、「サーバー X で 404 エラー」やその他の同様のメッセージが発生したことがユーザーに通知されます。ネットワークが遅くなるとインターフェースが応答しなくなるアプリもたくさんあります。このような状況は、ネットワーク障害モードが十分に理解されておらず、ネットワークの劣化や障害の可能性が予測されていないために発生します。このようなタイプのエラーを回避し、ネットワーク エラーを適切に処理できるようにするには、まずその原因を理解する必要があります。

デバイスからリモート サーバーにバイトがどのように送信され、リモート サーバーからデバイスにバイトがどのように受信されるかを考えてください。このプロセスには数百ミリ秒しかかかりませんが、ネットワーク機器が適切に動作する必要があります。デバイス ネットワークとネットワーク相互接続の複雑さにより、階層型ネットワークが作成されています。階層ネットワークは、この複雑な環境をより管理しやすいモジュールに分割します。これはプログラマにとっては便利ですが、データが層間を流れるときに前述のネットワーク エラーが発生する可能性があります。図 5-1 は、インターネット プロトコル スタックのさまざまな層を示しています。


図5-1

各層は、数学的、論理的、またはその他のタイプの検出である可能性がある、ある種のエラー検出を実行します。たとえば、ネットワーク インターフェイス層が特定のフレームを受信すると、まずエラー訂正コードによって内容が検証され、一致しない場合はエラーが発生します。フレームが到着しない場合は、タイムアウトまたは接続のリセットが発生します。エラー検出は、メッセージを構文的および意味的にチェックするアプリケーション層まで、スタックのすべての層で行われます。

iOS で URL ローディング システムを使用する場合、携帯電話とサーバー間の接続でさまざまな問題が発生する可能性がありますが、これらの原因はオペレーティング システム エラー、HTTP エラー、アプリケーション ミスの 3 つのエラー カテゴリに分類できます。これらのエラー カテゴリは、HTTP リクエストを作成する一連の操作に関連しています。図 5-2 は、企業ネットワークからデータを提供するアプリケーション サーバーへの HTTP リクエストの単純なシーケンス図を示しています。それぞれの影付きの領域は、これら 3 つのエラー タイプのエラー ドメインを表します。通常、オペレーティング システムのエラーは、HTTP サーバーの問題によって発生します。 HTTP エラーは、HTTP サーバーまたはアプリケーション サーバーが原因で発生します。アプリケーション エラーは、データ転送の要求、またはアプリケーション サーバーによる他のシステムのクエリによって発生します。


図5-2

リクエストが安全な HTTPS リクエストである場合、または HTTP サーバーがクライアントをリダイレクトしている場合、上記の一連の手順はより複雑になります。上記のステップの多くには、TCP 接続の確立に関与する SYN パケット シーケンスや SYN-ACK パケット シーケンスなど、多数のサブステップが含まれています。各エラー カテゴリについては、以下で詳しく説明します。

5.1.1 オペレーティングシステムエラー

オペレーティング システムのエラーは、パケットが意図した宛先に到達しないことが原因で発生します。パケットは接続の確立の一部である場合もあれば、接続の確立の途中である場合もあります。 OS エラーは次の原因で発生する可能性があります:

● ネットワークなし - デバイスにデータ ネットワーク接続がない場合、接続の試行はすぐに拒否されるか失敗します。この種のエラーは、Apple が提供する到達可能性フレームワークを通じて検出できます。これについては、このセクションで後ほど説明します。

● ターゲット ホストにルーティングできません - デバイスにはネットワーク接続がある可能性がありますが、接続されたターゲットは隔離されたネットワーク上にあるかオフラインである可能性があります。これらのエラーはオペレーティング システムによってすぐに検出される場合がありますが、接続がタイムアウトになる可能性もあります。

● 没有应用监听目标端口——在请求到达目标主机后,数据包会被发送到请求指定的端口号。如果没有服务器监听这个端口或是有太多的连接请求在排队,那么连接请求就会被拒绝。

● 无法解析目标主机名——如果无法解析目标主机名,那么URL加载系统就会返回错误。通常情况下,这些错误是由配置错误或是尝试访问没有外部名字解析且处于隔离网络中的主机造成的。

在iOS的URL加载系统中,操作系统错误会以NSError对象的形式发送给应用。iOS通过NSError在软件组件间传递错误信息。相比简单的错误代码来说,使用NSError的主要优势在于NSError对象包含了错误域属性。

不过,NSError对象的使用并不限于操作系统。应用可以创建自己的NSError对象,使用它们在应用内传递错误消息。如下代码片段展示的应用方法使用NSError向调用的视图控制器传递回失败信息:

-(id)fetchMyStuff:(NSURL*)url error:(NSError**)error
{
BOOL errorOccurred = NO;
 
// some code that makes a call and may fail
 
if(errorOccurred) //some kind of error
{
NSMutableDictionary *errorDict = [NSMutableDictionary dictionary];
[errorDictsetValue:@"Failed to fetch my stuff"
forKey:NSLocalizedDescriptionKey];
*error = [NSErrorerrorWithDomain:@"myDomain"
code:kSomeErrorCode
userInfo:errorDict];
return nil;
} else {
return stuff
}
 
}
ログイン後にコピー

域属性根据产生错误代码的库或框架对这些错误代码进行隔离。借助域,框架开发者无须担心覆盖错误代码,因为域属性定义了产生错误的框架。比如,框架A 与B 都会产生错误代码1,不过这两个错误代码会被每个框架提供的唯一域值进行区分。因此,如果代码需要区分NSError 值,就必须对NSError 对象的code 与domain 属性进行比较。

NSError 对象有如下3 个主要属性:
● code——标识错误的NSInteger 值。对于产生该错误的错误域来说,这个值是唯一的。
● domain —— 指定错误域的NSString 指针, 比如NSPOSIXErrorDomain 、NSOSStatusErrorDomain 及NSMachErrorDomain。
● userInfo——NSDictionary 指针,其中包含特定于错误的值。
URL 加载系统中产生的很多错误都来自于NSURLErrorDomain 域,代码值基本上都来自于CFNetworkErrors.h 中定义的错误代码。与iOS 提供的其他常量值一样,代码应该使用针对错误定义好的常量名而不是实际的错误代码值。比如,如果客户端无法连接到主机,那么错误代码是1004,并且有定义好的常量kCFURLErrorCannotConnectToHost。代码绝不应该直接引用1004,因为这个值可能会在操作系统未来的修订版中发生变化;相反,应该使用提供的枚举名kCFURLError。
如下是使用URL 加载系统创建HTTP 请求的代码示例:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">NSHTTPURLResponse *response=nil;
NSError *error=nil;
NSData *myData=[NSURLConnectionsendSynchronousRequest:request
returningResponse:&response
error:&error];
if (!error) {
// No OS Errors, keep going in the process
...
} else {
// Something low level broke
}</span></span>
ログイン後にコピー

注意,NSError 对象被声明为指向nil 的指针。如果出现错误,那么NSURLConnection对象只会实例化NSError 对象。URL 加载系统拥有NSError 对象;如果稍后代码会用到它,那么应该保持这个对象。如果在同步请求完成后NSError 指针依然指向nil,那就说明没有产生底层的OS 错误。这时,代码就知道没有产生OS 级别的错误,不过错误可能出现在协议栈的某个高层。
如果应用创建的是异步请求,那么NSError 对象就会返回到委托类的下面这个方法:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error</span></span>
ログイン後にコピー
这是传递给请求委托的最终消息,委托必须能识别出错误的原因并作出恰当的反应。在如下示例中,委托会向用户展UIAlertView:
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">- (void) connection:conndidFailWithError:error {
UIAlertView *alert = [UIAlertViewalloc] initWithTitle:@"Network Error"
message:[error description]
delegate:self
cancelButtonTitle:@"Oh Well"
otherButtonTitles:nil];
[alert show];
[alert release];
}</span></span>
ログイン後にコピー

上述代码以一种生硬且不友好的方式将错误展现给了用户。在iOS 人机界面指南(HiG)中,Apple 建议不要过度使用UIAlertViews,因为这会破坏设备的使用感受。5.3 节“优雅地处理网络错误”中介绍了如何通过良好的用户界面以一种干净且一致的方式处理错误的模式。
iOS 设备通信错误的另一主要原因就是由于没有网络连接而导致设备无法访问目标服务器。可以在尝试发起网络连接前检查一下网络状态,这样可以避免很多OS 错误。请记
住,这些设备可能会很快地进入或是离开网络。因此,在每次调用前检查网络的可达性是非常合情合理的事情。
iOS 的SystemConfiguration 框架提供了多种方式来确定设备的网络连接状态。可以在SCNetworkReachability 参考文档中找到关于底层API 的详尽信息。这个API 非常强大,不过也有点隐秘。幸好,Apple 提供了一个名为Reachability 的示例程序,它为SCNetworkReachability实现了一个简化、高层次的封装器。Reachability 位于iOS 开发者库中。
Reachability 封装器提供如下4 个主要功能:
● 标识设备是否具备可用的网络连接
● 标识当前的网络连接是否可以到达某个特定的主机
● 标识当前使用的是哪种网络技术:Wi-Fi、WWAN 还是什么技术都没用
● 在网络状态发生变化时发出通知要想使用Reachability API,请从iOS 开发者库中下载示例程序,地址是http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html,然后将Reachability.h与Reachability.m 添加到应用的Xcode 项目中。此外,还需要将SystemConfiguration 框架添加到Xcode 项目中。将SystemConfiguration 框架添加到Xcode 项目中需要编辑项目配置。图5-3 展示了将SystemConfiguration 框架添到Xcode 项目中所需的步骤。
(3) 选择SystemConfiguration.framework

高度な iOS ネットワーク プログラミング: iPhone および iPad エンタープライズ アプリケーション開発のエラー処理_PHP チュートリアル

选定好项目目标后,找到设置中的Linked Frameworks and Libraries,单击+按钮添加框架,这时会出现框架选择界面。选择SystemConfiguration 框架,单击add 按钮将其添加到项目中。
如下代码片段会检查是否存在网络连接。不保证任何特定的主机或IP 地址是可达的,只是标识是否存在网络连接。

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">#import "Reachability.h"
...
if([[Reachability reachabilityForInternetConnection]
currentReachabilityStatus] == NotReachable) {
// handle the lack of a network
}</span></span>
ログイン後にコピー

在某些情况下,你可能想要修改某些动作、禁用UI 元素或是当设备处于有限制的网络中时修改超时值。如果应用需要知道当前正在使用的连接类型,那么请使用如下代码:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">#import "Reachability.h"
...
NetworkStatus reach = [[Reachability reachabilityForInternetConnection]
currentReachabilityStatus];
if(reach == ReachableViaWWAN) {
// Network Is reachable via WWAN (aka. carrier network)
} else if(reach == ReachableViaWiFi) {
// Network is reachable via WiFi
}</span></span>
ログイン後にコピー

知道设备可达性状态的变化也是很有必要的,这样就可以主动修改应用行为。如下代码片段启动对网络状态的监控:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">#import "Reachability.h"
...
[[NSNotificationCenterdefaultCenter]
addObserver:self
selector:@selector(networkChanged:)
name:kReachabilityChangedNotification
object:nil];
Reachability *reachability;
reachability = [[Reachability reachabilityForInternetConnection] retain];
[reachability startNotifier];</span></span>
ログイン後にコピー

上述代码将当前对象注册为通知观察者,名为kReachabilityChangedNotification。
NSNotificationCenter 会调用当前对象的名为networkChanged:的方法。当可达性状态发生变化时,就向该对象传递NSNotification 及新的可达性状态。如下示例展示了通知监听者:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">- (void) networkChanged: (NSNotification* )notification
{
Reachability* reachability = [notification object];
第Ⅱ部分 HTTP 请求:iOS 网络功能
98
if(reachability == ReachableViaWWAN) {
// Network Is reachable via WWAN (a.k.a. carrier network)
} else if(reachability == ReachableViaWiFi) {
// Network is reachable via WiFi
} else if(reachability == NotReachable) {
// No Network available
}
}</span></span>
ログイン後にコピー

可达性还可以确定当前网络上某个特定的主机是否是可达的。可以通过该特性根据应用是处于内部隔离的网络上还是公开的Internet 上调整企业应用的行为。如下代码示例展示了该特性:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">Reachability *reach = [Reachability
reachabilityWithHostName:@"www.captechconsulting.com"];
if(reachability == NotReachable) {
// The target host is not reachable available
}</span></span>
ログイン後にコピー

请记住,该特性对目标主机的访问有个来回。如果每个请求都使用该特性,那就会极大增加应用的网络负载与延迟。Apple 建议不要在主线程上检测主机的可达性,因为尝试访问主机可能会阻塞主线程,这会导致UI 被冻结。
OS 错误首先就表明请求出现了问题。应用开发者有时会忽略掉它们,不过这样做是有风险的。因为HTTP 使用了分层网络,这时HTTP 层或是应用层可能会出现其他类型的潜在失败情况。

5.1.2 HTTP 错误
HTTP 错误是由HTTP 请求、HTTP 服务器或应用服务器的问题造成的。HTTP 错误通过HTTP 响应的状态码发送给请求客户端。
404 状态是常见的一种HTTP 错误,表示找不到URL 指定的资源。下述代码片段中的HTTP 头就是当HTTP 服务器找不到请求资源时给出的原始输出:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">HTTP/1.1 404 Not Found
Date: Sat, 04 Feb 2012 18:32:25 GMT
Server: Apache/2.2.14 (Ubuntu)
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 248
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1</span></span>
ログイン後にコピー

响应的第一行有状态码。HTTP 响应可以带有消息体,其中包含友好、用户可读的信息,用于描述发生的事情。你不应该将是否有响应体作为判断HTTP 请求成功与否的标志。
一共有5 类HTTP 错误:

● 信息性质的100 级别——来自于HTTP 服务器的信息,表示请求的处理将会继续,不过带有警告。
● 成功的200 级别——服务器处理了请求。每个200 级别的状态都表示成功请求的不同结果。比如,204 表示请求成功,不过没有向客户端返回负载。
● 重定向需要的300 级别——表示客户端必须执行某个动作才能继续请求,因为所需的资源已经移动了。URL 加载系统的同步请求方法会自动处理重定向而无须通知代码。如果应用需要对重定向进行自定义处理,那么应该使用异步请求。
● 客户端错误400 级别——表示客户端发出了服务器无法正确处理的错误数据。比如,未知的URL 或是不正确的HTTP 头会导致这个范围内的错误。
● 下游错误500 级别——表示HTTP 服务器与下游应用服务器之间出现了错误。比如,如果Web 服务器调用了JavaEE 应用服务器,Servlet 出现了NullPointerException,那么客户端就会收到500 级别的错误。
iOS 中的URL 加载系统会处理HTTP 头的解析,并可以轻松获取到HTTP 状态。如果代码通过HTTP 或HTTPS URL 发出了同步调用,那么返回的响应对象就是一个NSHTTPURLResponse 实例。NSHTTPURLResponse 对象的statusCode 属性会返回数值形式的请求的HTTP 状态。如下代码演示了对NSError 对象以及从HTTP 服务器返回的成功状态的验证:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">NSHTTPURLResponse *response=nil;
NSError *error=nil;
NSData *myData = [NSURLConnectionsendSynchronousRequest:request
returningResponse:&response
error:&error];
//Check the return
if((!error) && ([response statusCode] == 200)) {
// looks like things worked
} else {
// things broke, again.
}</span></span>
ログイン後にコピー

如果请求的URL不是HTTP,那么应用就应该验证响应对象是否是NSHTTPURLResponse对象。验证对象类型的首选方法是使用返回对象的isKindOfClass:方法,如下所示:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">if([response isKindOfClass:[NSHTTPURLResponse class]]) {
// It is a HTTP response, so we can check the status code
...</span></span>
ログイン後にコピー
要想了解关于HTTP 状态码的权威信息,请参考W3 RFC 2616,网址是http://www.w3.org/Protocols/rfc2616/rfc2616.html。
5.1.3 应用错误
本节将会介绍网络协议栈的下一层(应用层)产生的错误。应用错误不同于OS 错误或HTTP 错误,因为并没有针对这些错误的标准值或是原因的集合。这些错误是由运行在服
务层之上的业务逻辑和应用造成的。在某些情况下,错误可能是代码问题,比如异常,不过在其他一些情况下,错误可能是语义错误,比如向服务提供了无效的账号等。对于前者来说,建议生成HTTP 500 级别的错误;对于后者来说,应该在应用负载中返回错误码。
比如,如果用户尝试从账户中转账的金额超出了账户的可用余额,那么手机银行就应该报告应用错误。如果发出了这样的请求,那么OS 会说请求成功发送并接收到了响应。HTTP 服务器会报告接收到了请求并发出了响应,不过应用层必须报告这笔交易失败。报告应用错误的最佳实践是将应用的负载数据封装在标准信封中,信封中含有一致的应用错误位置信息。在上述资金转账示例中,成功的转账响应的业务负载应该如下所示:

<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">{ "transferResponse":{
"fromAccount":1,
"toAccount":5,
"amount":500.00,
"confirmation":232348844
}
}</span></span>
ログイン後にコピー
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">
</span></span>
ログイン後にコピー


响应包含了源账号与目标账号、转账的资金数额及确认号。直接将错误码与错误消息放到transferResponse 对象中会导致错误码与错误消息的定位变得困难。如果每个动作都将错误信息放到自己的响应对象中,就无法在应用间重用错误报告逻辑了。使用如下代码中的数据包结构可以让应用快速确定是否出现了错误,方式是检查响应的JSON 负载中是否存在“error”对象:


<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;">{"error":{
"code":900005,
"messages":"Insufficient Funds to Complete Transfer"
},
"data":{
"fromAccount":1,
"toAccount":5,
"amount":500.00
}
}</span></span>
ログイン後にコピー


エラー情報は常に応答ペイロードの error 属性に配置されるため、エラーを報告する UI コードは簡単に再利用できます。さらに、実際のトランザクションのロード処理は、常に同じ属性名の下にあるため簡素化されます。
リクエストが失敗した理由が OS、HTTP 層、アプリケーションのいずれであっても、アプリケーションは応答方法を知っている必要があります。開発中にアプリケーションのすべての障害モードを事前に検討し、エラーを検出して対応する一貫した方法を設計する必要があります。

5.2 エラー処理の経験則
エラーはさまざまな理由で発生する可能性があり、それらを処理する最適な方法は、作成しているアプリケーションによって異なります。複雑ではありますが、エラー原因の制御不能な性質に対処するのに役立つ経験則がいくつかあります。
5.2.1 インターフェースコントラクトのエラー処理
サービス インターフェイスを設計するときに、入力、出力、サービス操作のみを指定するのは誤りです。インターフェイス コントラクトでは、エラー メッセージがクライアントに送信される方法も指定する必要があります。サービス インターフェイスでは、可能な場合は業界標準の方法を使用してエラー情報を伝達する必要があります。たとえば、サーバーはサーバー側の障害に対して新しい HTTP ステータス値を定義するのではなく、適切な 500 レベルのステータスを使用する必要があります。標準値を使用すると、クライアントとサーバーの開発者はエラー メッセージの伝達方法について合意できます。アプリケーションは、エラーが発生したかどうかを判断するために、非標準のステータスやその他のプロパティ値に依存してはなりません。
また、アプリケーション開発者は、エラーの処理方法を決定する際に、現在のサーバー ソフトウェア スタックの動作に依存しないでください。 iOS アプリの展開後、将来のアップグレードや置き換えにより、サーバー ソフトウェア スタックの動作が変更される可能性があります。
5.2.2 エラーステータスが正しくない可能性があります
モバイル Web には、従来の Web アプリケーションのエラーとは異なり、あまり目立たない動作があります: あいまいなエラー報告。モバイルデバイスからサーバーへのネットワークリクエストには、次の 3 つの結果が考えられます:
● デバイスは、操作が成功したことを確認する完全な機能を備えています。たとえば、NSError と HTTP ステータス値は両方とも成功を示し、返されたペイロードには意味的に正しい情報が含まれています。
● デバイスは、操作が失敗したことを確認する機能を十分に備えています。たとえば、返されたアプリケーション ペイロードには、この操作に固有のサーバーからの障害識別子が含まれています。
● デバイスは操作が失敗したことを漠然と認識しています。たとえば、モバイル アプリは、2 つのアカウント間で送金するための HTTP リクエストを作成します。リクエストは銀行システムによって受信され、正しく処理されましたが、ネットワーク障害により応答が失われ、NSURLConnection がタイムアウトを報告しました。転送リクエストが成功した後にタイムアウトが発生しました。操作を再試行すると、転送が重複して行われ、アカウントが超過引き出される可能性があります。シナリオ 3 では、アプリケーションの予期しない、検出されていない誤った動作が発生する可能性があります。アプリケーション開発者がシナリオ 3 の存在を知らない場合、操作が失敗したと誤って想定し、成功した操作を誤って再試行する可能性があります。操作全体が失敗したことを知るだけでは十分ではありません。開発者は、リクエストが失敗した原因と、失敗した各リクエストを自動的に再試行することが適切かどうかを検討する必要があります。
5.2.3 検証ペイロード
アプリケーション開発者は、OS エラーや HTTP エラーがない場合、ペイロードが有効であると想定しないでください。多くのシナリオでは、リクエストは成功したように見えますが、ペイロードが無効です。クライアントとサーバーの間で渡されるペイロードは検証メカニズムです。 JSON と XML は検証メカニズムを備えたペイロード形式ですが、カンマ区切り値ファイルと HTML には検証メカニズムがありません。
5.2.4 エラーを通常のビジネス状況から分離する
サービス契約では、通常のビジネス状況をエラーとして報告してはなりません。たとえば、不正行為の可能性によりユーザーのアカウントがロックされている場合、ロック ステータスはデータ ペイロードで報告される必要があり、エラー状態として扱われるべきではありません。エラーを通常のビジネス条件から分離すると、コードで懸念事項の適切な分離を維持できるようになります。何か問題が発生した場合にのみ、それをエラーとみなすべきです。
5.2.5 HTTPステータスを常に確認する
同じサービスを繰り返し呼び出す場合でも、HTTP 応答の HTTP ステータスを常にチェックして、成功ステータスの値を理解してください。サーバーの状態はいつでも、たとえ並列呼び出しの間であっても変化する可能性があります。
5.2.6 NSError 値を常に確認する
アプリケーション コードは、返された NSError 値を常にチェックして、OS レベルで問題が発生していないことを確認する必要があります。アプリが常に信号が良好な Wi-Fi ネットワーク上で実行されることがわかっている場合でも、これを実行する必要があります。すべてがうまくいかない可能性があり、ネットワークを扱うときはコードが防御的である必要があります。
5.2.7 一貫したアプローチを使用してエラーを処理する
ネットワークエラーの原因は数多くあり、すべてをリストすることは困難であり、影響の多様性と範囲も非常に大きくなります。アプリケーションを設計するときは、一貫したユーザー インターフェイス パターンや一貫した名前付けパターンだけに焦点を当てないでください。また、ネットワーク エラーを処理するための一貫したパターンを設計する必要もあります。パターンでは、アプリケーションで発生する可能性のあるすべての種類のエラーを考慮する必要があります。これらのエラーがアプリケーション内部で一貫したパターンで処理されない場合、アプリケーションはこれらのエラーを一貫した方法でユーザーに報告できません。
5.2.8 常にタイムアウトを設定する
iOS では、HTTP リクエストのデフォルトのタイムアウト間隔は 4 分ですが、これはモバイル アプリケーションには長すぎ、ほとんどのユーザーはどのアプリケーションでも 4 分も待つことはありません。开发者需要选择合理的超时时间,方式是评估网络请求的可能响应时间,然后将最差的网络场景下的网络延迟考虑进去。如下示例展示了如何创建具有20 秒超时时间的请求:
<span style="font-family:Microsoft YaHei;font-size:14px;">- (NSMutableURLRequest *) createRequestObject:(NSURL *)url {
NSMutableURLRequest *request = [[[NSMutableURLRequestalloc]
initWithURL:url
cachePolicy:NSURLCacheStorageAllowed
timeoutInterval:20
autorelease];
return request;
}</span>
ログイン後にコピー

5.3 优雅地处理网络错误
iOS 简化了网络通信,不过对可能发生的所有类型的错误与边界条件作出响应则不是那么轻松的事情。常见的做法是在网络代码中放置钩子来快速查看结果,接下来再对所有的错误情况进行处理。对于非移动应用来说,通常可以使用这种方式,因为来自工作站的网络连接是可预测的。如果在应用加载时有网络,那么当用户加载下一个页面时基本上也会有网络。绝大多数情况都是这样的,开发者可以依赖浏览器向用户显示消息。如果在移动应用中没有及时添加异常处理,那么当后面遇到新的错误源时就需要大幅重构网络代码。
本节将会介绍一种设计模式,用来创建一个优雅且健壮的异常处理框架,并且在未来遇到新的错误时几乎不需要做什么工作就能很好地进行扩展。考虑如下3 个移动通信中的主要异常情况:
● 由于设备没有充分的网络连接导致远程服务器不可达。
● 由于OS 错误、HTTP 错误或是应用错误导致远程服务器返回错误响应。
● 服务器需要认证,而设备尝试发出未认证的请求。
随着可能的异常数量呈现出线性增长,处理这些异常的代码量则呈指数级增长。如果代码要在每一类请求中处理所有这些错误,那么代码的复杂性与数量就会呈指数级增长。本节将要介绍的模式会将这种指数级的曲线压成线性曲线。
5.3.1 设计模式介绍
本节介绍的模式联合使用了指挥调度模式与广播通知。该模式包含如下类型的对象:
● 控制器
● 命令对象
● 异常监听器
● 命令队列
下面从高层次来介绍每一类对象的行为。
1. 对象说明
下面介绍构成指挥调度模式的对象的特性及属性。
控制器
控制器通常指的是视图控制器,用来请求数据并处理结果。在该设计模式中,控制器无须包含任何异常处理逻辑。唯一需要控制器处理的错误情况就是成功完成或是完全不可恢复的服务失败。在不可恢复失败这个场景中,控制器通常会将自己从视图栈中弹出,因为用户这时已经收到接下来要介绍的异常监听器对象发出的失败通知了。控制器会创建命令并监听命令的完成情况。
命令对象
命令对象与应用执行的不同网络交易相关。检索图片、从指定的REST 端点处获取JSON 数据或是向服务发出POST 信息等都是命令对象请求。命令对象是NSOperation 的子类。由于命令对象中的大多数逻辑都与其他类型的命令对象相同,因此可以创建父类命令象来处理,让特定的命令继承该逻辑。命令对象具有如下属性:
● 完成通知名——在iOS 中,控制器会将自身注册为该通知名的观察者。当服务调用成功返回时,命令对象会通过NSNotificationCenter 使用该名字来广播通知。虽然该名字对于命令类来说通常是唯一的,不过在某些情况下,如果有多个控制器发出相同类型的命令(区分不同的响应),那么这个名字针对于特定的实例将是唯一的。
● 服务器错误异常通知名——特定的异常处理器对象会监听该通知。当服务器超时、返回与认证相关的OS 错误或HTTP 错误时,命令对象会通过NSNotificationCenter并使用该名字来广播消息。通常情况下,所有的命令类会共享相同的异常名,因此也会共享相同的异常监听器。不过不同的命令类可能需要使用不同的异常监听器,并且有不同的服务器错误异常名。
● 可达性异常通知名——当检测到无法到达Internet 或目标主机时,命令对象会生成该类型的通知。另一个异常监听器可以监听该类型的异常。在某些应用中,这类常是不需要的,因为服务器错误异常监听器会处理可达性异常。
● 认证异常通知名——如果确定用户没有认证或是服务器报告了未认证状态,那么命令对象可能会产生该类型的通知。第3 个异常监听器会等待该类型通知的出现。认证通知名通常会在应用的所有通知中共享。
● 自定义属性——这些属性特定于发出的请求。控制器通常会提供这些值,因为它们特定于服务调用所需的业务数据,而且不同的调用数据也是不同的。异常监听器
一般来说,每个异常监听器都是由应用委托实例化的,位于后台并等待着特定类型的通知。多くの場合、例外リスナーは、通知を受信したときにモーダル ビュー コントローラーを表示します。これについては、「例外リスナーの動作」セクションで説明します。
コマンドキュー
コントローラーは処理のためにコマンドをコマンド キューに送信します。アプリケーションには 1 つ以上のコマンド キューがある場合があります。 iOS では、コマンド キューは NSOperationQueue のサブクラスです。メイン キューはコマンド キューとして使用しないでください。メイン キューの操作はユーザー インターフェイス スレッドで実行され、長時間実行される操作を実行する際のユーザー エクスペリエンスに影響を与えます。
NSOperationQueues は、アクティブな操作と操作間の依存関係を管理するための組み込み機能を提供します。
2. オブジェクト 上記の各オブジェクトは、オンライン トランザクションを正常に完了する上で異なる役割を果たします。このモードにおけるそれぞれの役割を以下に紹介します。
コントローラーの動作
コントローラーは、UI とビジネス ロジックの実行に重点を置きます。コントローラーがサービスからデータを取得したい場合は、次のアクションを実行する必要があります:
(1) ネットワークコマンドオブジェクトを作成します。
(2) コマンドオブジェクトの特定の属性の初期化要求。
(3) コマンド完了のオブザーバーとして登録します。
(4) コマンドを操作キューにプッシュし、実行の準備をします。
(5) NSNotificationCenter から完了通知が送信されるまで待ちます。
操作が完了すると、コントローラーは完了通知を受け取り、次のアクションを実行します:
(1) 動作ステータスを確認して、動作が成功したかどうかを確認します。
(2) 成功した場合、コントローラは受信したデータを処理します。受信したデータは、NSNotification オブジェクトの userInfo プロパティを通じてコン​​トローラーに提供されます。 NSOperationQueues は、独自のスレッドで NSOperation オブジェクトを実行します。操作が完了すると、NSNotificationCenter を通じて NSNotification が送信されます。通知コールバック メソッドは、NSOperation が実行されているスレッド上で呼び出されます。これにより、この場合、通知コールバック メソッドがメイン スレッドに入らないことが保証されます。コントローラーが UI を操作する場合、これらの変更はメインスレッドで (通常は Grand CentralDispatch (GCD) を通じて) 行う必要があります。
(3) 失敗した場合、コントローラにはアプリケーションの要件に応じて多くのオプションがあります。たとえば、ビュー スタックから自分自身をポップアウトしたり、UI を更新してデータが利用できないことを示すことができます。このため、コントローラーは再試行したり、モーダル警告を表示したりすべきではありません
これらのアクションは例外リスナーの責任です。 (4) コントローラーは、コマンド完了通知のオブザーバーから自身を削除する必要があります。コントローラーが同じタイプのコマンドからの他のデータを監視したい場合、これは必要ない場合があります。コントローラーには再試行、タイムアウト、認証、到達可能性を処理するロジックが含まれていないことに注意してください。このロジックはコマンド リスナーと例外リスナーによって実装されます。
コントローラーが返されたデータのみを確実に受信するようにしたい場合は、通知名をキューに入れる前にそのコマンド オブジェクト インスタンスに固有の値に変更し、その名前の通知をリッスンする必要があります。コマンド オブジェクトの動作 コマンド オブジェクトは、ターゲット サービスを呼び出し、サービス呼び出しの結果をブロードキャストする責任があります。一般に、コマンド オブジェクトは次の手順を実行する必要があります:
(1) 到達可能性を確認します。ネットワークに到達できない場合は、到達可能性例外通知がブロードキャストされます。
(2) 必要に応じて認証状況を確認します。ユーザーが許可されていない場合、到達可能性例外通知がブロードキャストされます。
(3) コントローラーによって提供されるカスタム属性を使用してネットワーク リクエストを構築します。通常、エンドポイント URL はコマンド オブジェクト クラスの静的プロパティであるか、構成サブシステムからロードされます。
(4) 同期リクエスト方式を使用してネットワークリクエストを発行します。詳細については、第 3 章、セクション 3.3.2「同期リクエスト」を参照してください。
(5) リクエスト状況を確認します。ステータスがOSエラーまたはHTTPエラーの場合、サーバー例外通知がブロードキャストされます。認証エラーの場合は、認証例外通知をブロードキャストします。
(6) 分析結果については、第 4 章を参照してください。
(7) 成功ステータスの完了通知をブロードキャストします。
コマンド オブジェクトが通知をブロードキャストする場合、それが成功かその他の通知であるかに関係なく、辞書オブジェクトには、それ自体のコピー、呼び出しステータス、および呼び出しの結果として返されたデータが含まれている必要があります。 NSOperation インスタンスは 1 回しか実行できないため、それ自体をレプリケートする必要があります。後で説明するように、リスナーが例外を処理している間にコマンドが再送信される可能性があります。
コマンドはメイン スレッドではなくバックグラウンド スレッドで実行されるため、同期リクエスト API はこのパターンに適しています。リクエストがメモリ内で処理できると予想される量を超えるデータを作成または返した場合、アプリケーションは非同期リクエストを使用する必要があります。 NSOperation のメイン関数はメソッドであるため、オペレーションはメインをブロックする同時ロックを実装する必要があります。 非同期呼び出しが完了するまでメソッドを実行します。
リスナーの異常な行動
例外リスナーは、このモデルを非常に強力にする上で重要な役割を果たします。これらのオブジェクトは通常、アプリケーション デリゲートによって作成され、メモリ内に常駐し、通知を待機します。通知を受信すると、リスナーはユーザーに通知し、ユーザーからの応答を受け取ることもあります。例外が発生すると、通知には例外をトリガーしたコマンドのコピーが含まれます。ユーザーが応答すると、リスナーは通常、コマンドをキューに送り返して再試行します。例外リスナーに関する興味深い点は、複数のコマンドが同時に発生する可能性があるため、ユーザーが最初の例外に応答するときに複数の例外通知が同時に生成される可能性があることです。出于这一点,异常监听器必须收集异常通知,然后在用户响应完第一个异常后重新提交所有的触发命令。这个错误集合可以避免一种常见的应用行为不当——用户被相同问题触发的多个UIAlertView 连续轰炸。 服务器异常的流程如下所示:
(1) 呈现一个漂亮的模态对话框,列出错误信息并让用户选择取消或是重试。
(2) 收集可能被广播的其他服务器异常。
(3) 如果用户选择重试,那么关闭对话框并重新提交所有收集到的命令。
(4) 如果用户选择取消,那么关闭对话框。监听器应该将所有收集到的命令的完成状态设为失败,然后让每个命令广播一条完成通知。
可达性异常的流程如下所示:
(1) 呈现一个漂亮的模态对话框,通知用户需要网络连接。
(2) 收集可能会被广播的其他服务异常。
(3) 监听可达性变更。当网络可达时,关闭对话框并重新提交收集到的命令。认证异常的流程稍微有点复杂。请记住,命令之间是独立的,在任意时刻可能会有多个命令同时发生。认证流程并不会生成认证异常通知,流程如下所示:
(1) 呈现一个模态登录视图。
(2) 继续收集由于认证错误导致的失败命令。
(3) 如果用户取消,那么监听器应该针对收集到的命令使用失败状态发送一条完成通知。
(4) 如果用户提供了认证信息,那么创建一个登录命令,将其放到命令队列中。
(5) 等待登录命令的完成通知。
(6) 如果由于用户名/密码不匹配而导致登录失败,那么回到步骤(2),否则关闭登录视图控制器。
(7) 如果登录命令成功,那么重新向命令队列提交触发命令。
(8) 如果登录命令失败,那么让触发命令使用失败状态发送一条完成通知。
命令队列行为
命令队列是原生的iOSNSOperationQueue 对象。在默认情况下,命令队列遵循着先进
先出(FIFO)的顺序。在代码向NSOperationQueue 中添加命令对象后,执行如下动作: (1) 保持命令对象,这样其内存就不会被释放掉。
(2) 等待,直到队列头有可用位置。
(3) 当命令对象到达队列头时,命令对象的start 方法会被调用。
(4) 命令对象的main 方法得到调用。
请参考iOS API 文档中关于NSOperation 与NSOperationQueue 对象的介绍来了解队列与命令对象之间交互的详细信息。
5.3.2 指挥调度模式示例
本节通过调用YouTube 的一项认证服务来介绍指挥调度模式。在此类通信过程中需要考虑很多失败模式:
● 用户可能没有提供有效的身份信息。
● 设备可能无法联网。
● YouTube 可能没有及时响应或是出于某些原因失败了。
应用需要以一种优雅且可靠的方式处理每一种情况。该例将会阐述主要的代码组件并介绍一些实现细节。项目中的应用是个示例应用,只用于演示目的。
1. 前提条件
要想成功运行该应用,你需要准备好如下内容:
● 一个YouTube 账号。
● 至少向你的YouTube 账号上传一个视频(无须公开,只要上传到该账号即可)。
● 从Wrox 网站上下载的项目压缩文件。
该项目使用Xcode 4.1 与iOS 4.3 开发,应用使用的是截止到2011 年10 月份的YouTubeAPI,不过该API 处于Google 的控制下,而且可能会发生变化。
2. 主要对象
下载好项目并在Xcode 中加载后,你会看到如下类:
1) 命令
命令分组中有如下一些类。
BaseCommand
BaseCommand 是所有命令对象的父类。它提供了每个命令类所需的众多方法,这些方法有:
● 发送完成、错误与登录通知的方法。
● 用于让对象监听完成通知的方法。
● 用于支持实际的NSURLRequests 的方法。
BaseCommand 继承了NSOperation,因此所有的命令逻辑都位于该类的每个子类对象的main 方法中。
GetFeed
如代码清单5-1 所示,该类的main 方法会调用YouTube 并加载当前登录用户上传的视频列表。YouTube 通过请求HTTP 头中的令牌来确定登录用户的身份。如果没有这个头,YouTube 就会返回HTTP 状态码0 而不是更加标准的4xx HTTP 错误。
代码清单5-1 CommandDispathDemo/service-interface/GetFeed.h
<span style="font-family:Microsoft YaHei;font-size:14px;">- (void)main {
NSLog(@"Starting getFeed operation");
// Check to see if the user is logged in
if([self isUserLoggedIn]) { // only do this if the user is logged in
// Build the request
NSString *urlStr =
@"https://gdata.youtube.com/feeds/api/users/default/uploads";
NSLog(@"urlStr=%@",urlStr);
NSMutableURLRequest *request =
[ self createRequestObject:[NSURL URLWithString:urlStr]];
// Sign the request with the user’s auth token
[self signRequest:request];
// Send the request
NSHTTPURLResponse *response=nil;
NSError *error=nil;
NSData *myData = [self sendSynchronousRequest:request
response_p:&response
error:&error];
// Check to see if the request was successful
if([super wasCallSuccessful:responseerror:error]) {
[self buildDictionaryAndSendCompletionNotif: myData];
}
}
}</span>
ログイン後にコピー

在上述代码清单中,通过self 调用的很多方法都是在BaseCommand 父类中实现的。GetFeed 命令就是指挥调度模式的原型。main 方法会对用户登录进行检查,因为如果这个调用失败了,那就没必要再调用服务器了。如果用户已经登录,那么代码就会构建请求,将认证头添加到请求中,然后发送一条同步请求。代码的最后一部分会调用一个父类方法来确定调用是否成功。该方法使用来自于NSHTTPURLResponse 对象的NSError 对象与HTTP 状态码来确定是否成功。如果调用失败,就会广播一条错误通知或是需要登录的通知。
LoginCommand
该命令会向YouTube 发出对用户进行认证的请求。该命令比较独立,因为并没有使用BaseCommand 对象的辅助方法。之所以没有使用这些方法,是因为如果登录失败,就不应该生成需要认证的失败消息,而只会报告正常完成或是失败的状态。
登录监听器会处理来自于登录失败的错误。要想了解关于YouTube 所需协议的详细信息,请参考 http://code.google.com/apis/youtube/2.0/developers_guide_protocol_understanding_video_feeds.html。
2) 异常监听器
监听器分组中有视图控制器, 当错误发生或是用户需要登录时会呈现出来。NetworkErrorViewController 与LoginViewController 都继承了InterstitialViewController,后者提供了几个常用的辅助方法。这两个视图控制器都会以模态视图控制器的形式呈现出来。
● NetworkErrorViewController:向用户提供重试或是放弃失败操作的选择。如果用户选择重试,那么失败命令就会放回到操作队列中。
● LoginViewController:向用户请求用户名与密码。位于视图栈的顶部,直到用户成功登录为止。
● InterstitialViewController:作为其他异常监听器的父监听器,提供了一些支持功能,比如收集多个错误通知以及当错误解析完毕时重新分发错误的代码等。监听器的关键代码位于viewDidDisappear:方法中(如代码清单5-2 所示),当视图完全消失时会调用该方法。如果在视图完全消失前命令已进入队列中,那么其他错误就有可能导致再一次呈现视图,这会导致应用出现严重的错误。iOS 5 提供了处理这个问题的更好方式,因为在视图消失时用户可以指定执行的代码块。在处理触发命令前,代码并不需要确定消失的原因。

代码清单5-2 CommandDispatchDemo/NetworkErrorViewController.m
<span style="font-family:Microsoft YaHei;font-size:14px;">- (void) viewDidDisappear:(BOOL)animated {
if(retryFlag) {
// re-enqueue all of the failed commands
[self performSelectorAndClear:@selector(enqueueOperation)];
} else {
// just send a failure notification for all failed commands
[self performSelectorAndClear:
@selector(sendCompletionFailureNotification)];
}
self.displayed = NO;
}</span>
ログイン後にコピー
应用委托会将自身注册为网络错误与需要登录通知的监听器(如代码清单5-3 所示),收集异常通知并在错误发生时管理正确的视图控制器的呈现。上述代码展示了需要登录通知的通知处理器。由于要处理用户界面,因此其中的内容必须使用GCD 在主线程中执行。
代码清单5-3 CommandDispatchDemo/CommandDispatchDemoAppDelegate.m
<span style="font-family:Microsoft YaHei;font-size:14px;">/**
* Handles login needed notifications generated by commands
**/
- (void) loginNeeded:(NSNotification *)notif {
// make sure it all occurs on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
// make sure only one thread adds a command at a time
@synchronized(loginViewController) {
[loginViewController addTriggeringCommand:
[notif object];
if(!loginViewController.displayed) {
// if the view is not displayed then display it.
[[self topOfModalStack:self.window.rootViewController]
presentModalViewController:loginViewController
animated:YES];
}
loginViewController.displayed = YES;
}
}); // End of GC Dispatch block
}</span>
ログイン後にコピー

3) 视图控制器
在这个简单的应用中有个主要的视图控制器。RootViewController(参见下面的代码)继承了UITableViewController。当该控制器加载时,会创建并排队命令以加载用户的视频列表(又叫做YouTube 种子),并且会将控制流放回到主运行循环中以耐心等待命令的完成。
第一次调用总是失败的,因为这时用户还没有登录。CommandDispatchDemo/RootViewController.m 的requestVideoFeed 方法会启动加载视频列表的过程,如下所示:
<span style="font-family:Microsoft YaHei;font-size:14px;">(void)requestVideoFeed {
// create the command
GetFeed *op = [[GetFeedalloc] init];
// add the current authentication token to the command
CommandDispatchDemoAppDelegate *delegate =
(CommandDispatchDemoAppDelegate *)[[UIApplication
sharedApplication] delegate ];
op.token = delegate.token;
// register to hear the completion of the command</span>
ログイン後にコピー
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="background-color: rgb(255, 255, 255);">[op listenForMyCompletion:self selector:@selector(gotFeed:)];</span></span>
ログイン後にコピー
<span style="font-family:Microsoft YaHei;font-size:14px;">// put it on the queue for execution
[op enqueueOperation];
[op release];
}</span>
ログイン後にコピー

注意,代码并不需要检查用户是否已经登录;在执行时命令会做检查。
gotFeed:方法会处理来自于YouTube 的最终返回数据。在此例中,requestVideoFeed:方法会将gotFeed:方法注册为完成通知的目标方法。如果调用成功,该方法会将数据加载
到表视图中,否则显示UIAlertView:
<span style="font-family:Microsoft YaHei;font-size:14px;">- (void) gotFeed:(NSNotification *)notif {
NSLog(@"User info = %@", notif.userInfo);
BaseCommand *op = notif.object;
if(op.status == kSuccess) {
self.feed = op.results;
// if entry is a single item, change it to an array,
// the XML reader cannot distinguish single entries
// from arrays with only one element
id entries = [[feed objectForKey:@"feed"] objectForKey:@"entry"];
if([entries isKindOfClass:[NSDictionary class]]) {
NSArray *entryArray = [NSArrayarrayWithObject:entries];
[[feed objectForKey:@"feed"] setObject:entryArrayforKey:@"entry"];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableViewreloadData];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertViewalloc]
initWithTitle:@"No Videos"
message:@"The login to YouTube failed"
delegate:self
cancelButtonTitle:@"Retry"
otherButtonTitles:nil];
[alert show];
[alert release];
});
}
}
YouTubeVideoCell 是UITableViewCell 的子类,它会异步加载视频的缩略图。它通过LoadImageCommand 对象完成加载处理:
/**
* Start the process of loading the image via the command queue
**/
- (void) startImageLoad {
LoadImageCommand *cmd = [[LoadImageCommandalloc] init];
cmd.imageUrl = imageUrl;
// set the name to something unique
cmd.completionNotificationName = imageUrl;
[cmd listenForMyCompletion:self selector:@selector(didReceiveImage:)];
[cmdenqueueOperation];
[cmd release];
}</span>
ログイン後にコピー
这个类会改变完成通知名,这样它(也只有它)就可以接收到特定图片的通知了。否则,它还需要检查返回的通知来确定是否是之前发出的命令。
指挥调度模式的优雅之处在于能将应用中所有凌乱的异常处理逻辑和登录呈现逻辑与主视图控制器分离开来。当视图控制器发出命令时,会忽略掉所有的异常处理与认证处理,只是完成请求而已。只是发出请求,等待响应,然后处理响应。并不关心用户注册的请求是不是重试了5 次才成功。此外,服务请求代码并不需要知道请求来自于哪里,结果去向哪里;只是关注于执行调用并广播结果。 指挥调度模式还有其他优势,开发者一开始会编写一些代码并论证结果,如果顺利,那么会添加异常处理器,而这对之前的代码不会造成任何影响。此外,如果设计恰当,那么所有的网络服务调用都会使用相同的基础命令类,这会减少命令类的数量。 在通用应用中,可以通过异常监听器调整展示的视图,这样iPhone 上的错误显示界面就会适配于该平台,iPad 上的错误显示界面也会适配于更大的平台。 这种模式可以快速展示结果,对业务逻辑与异常处理进行关注分离,减少重复代码以及提供更好的用户体验。

5.4 まとめ
コードがネットワークを使用する場合、エラーの原因は数多くあります。エラーの原因を理解すると、ネットワークの問題を迅速に診断して解決することができます。 Reachability フレームワークを使用すると、コードはネットワーク状態の変化に積極的に対応して、不要なネットワーク エラーを回避できます。ネットワークリクエストを作成し、成功と失敗の結果を処理するときに一貫したパターンに従うことで、コードがよりクリーンで保守しやすくなります。

電子書籍「Advanced iOS Network Programming: E​​nterprise Application Development for iPhone and iPad」は無料で提供されています。必要な場合は、電子メール アドレスを残してください。空きができ次第お送りします。 いいねすることを忘れないでください!
WeChat: チンファシュヨウ クリックして最新の書籍をさらに表示してください


もっと見る 0

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

WLAN拡張モジュールが停止しました[修正] WLAN拡張モジュールが停止しました[修正] Feb 19, 2024 pm 02:18 PM

Windows コンピュータの WLAN 拡張モジュールに問題がある場合、インターネットから切断される可能性があります。この状況はイライラすることがよくありますが、幸いなことに、この記事では、この問題を解決し、ワイヤレス接続を再び正常に動作させるのに役立ついくつかの簡単な提案を提供します。 WLAN 拡張モジュールが停止しました。 WLAN 拡張モジュールが Windows コンピュータで動作を停止した場合は、次の提案に従って修正してください。 ネットワークとインターネットのトラブルシューティング ツールを実行して、ワイヤレス ネットワーク接続を無効にし、再度有効にします。 WLAN 自動構成サービスを再起動します。 電源オプションを変更します。 変更します。詳細な電源設定 ネットワーク アダプター ドライバーを再インストールする いくつかのネットワーク コマンドを実行する それでは、詳しく見てみましょう

操作を完了できません (エラー 0x0000771) プリンター エラー 操作を完了できません (エラー 0x0000771) プリンター エラー Mar 16, 2024 pm 03:50 PM

プリンターの使用中に、操作を完了できませんでした (エラー 0x00000771) などのエラー メッセージが表示された場合は、プリンターが切断されている可能性があります。この場合、以下の方法で問題を解決できます。この記事では、Windows 11/10 PC でこの問題を解決する方法について説明します。エラー メッセージ全体には、「操作を完了できませんでした (エラー 0x0000771)」が示されています。指定されたプリンターは削除されました。 Windows PC で 0x00000771 プリンター エラーを修正する プリンター エラーを修正するには、操作を完了できませんでした (エラー 0x0000771)。指定されたプリンターは Windows 11/10 PC で削除されています。次の解決策に従ってください: 印刷スプールを再起動します

HTTPステータスコード460の原因を明らかにする HTTPステータスコード460の原因を明らかにする Feb 19, 2024 pm 08:30 PM

HTTP ステータス コード 460 の復号化: このエラーはなぜ発生しますか?はじめに: 日常のネットワーク使用では、HTTP ステータス コードを含むさまざまなエラー プロンプトに遭遇することがよくあります。これらのステータス コードは、リクエストの処理を示すために HTTP プロトコルによって定義されたメカニズムです。これらのステータス コードの中には、比較的まれなエラー コード 460 があります。この記事では、このエラー コードを詳しく説明し、このエラーが発生する理由を説明します。 HTTP ステータス コード 460 の定義: まず、HTTP ステータス コードの基本を理解する必要があります。

Windows 10 の右下に地球が表示されてインターネットにアクセスできない場合はどうすればよいですか? Win10 で地球がインターネットにアクセスできない問題のさまざまな解決策 Windows 10 の右下に地球が表示されてインターネットにアクセスできない場合はどうすればよいですか? Win10 で地球がインターネットにアクセスできない問題のさまざまな解決策 Feb 29, 2024 am 09:52 AM

この記事では、Win10のシステムネットワーク上に地球儀マークが表示されるがインターネットにアクセスできない問題の解決策を紹介します。この記事では、地球がインターネットにアクセスできないことを示す Win10 ネットワークの問題を読者が解決するのに役立つ詳細な手順を説明します。方法 1: 直接再起動する まず、ネットワーク ケーブルが正しく接続されていないこと、ブロードバンドが滞っていないかを確認します。ルーターまたは光モデムが停止している可能性があります。この場合は、ルーターまたは光モデムを再起動する必要があります。コンピュータ上で重要な作業が行われていない場合は、コンピュータを直接再起動できます。ほとんどの軽微な問題は、コンピュータを再起動することですぐに解決できます。ブロードバンドが滞っておらず、ネットワークが正常であると判断される場合は、別の問題です。方法 2: 1. [Win]キーを押すか、左下の[スタートメニュー]をクリックし、表示されるメニュー項目の電源ボタンの上にある歯車アイコンをクリックし、[設定]をクリックします。

Windows Update プロンプト エラー 0x8024401c エラーの解決策 Windows Update プロンプト エラー 0x8024401c エラーの解決策 Jun 08, 2024 pm 12:18 PM

目次 解決策 1 解決策 21. Windows Update の一時ファイルを削除します。 2. 破損したシステム ファイルを修復します。 3. レジストリ エントリを表示および変更します。 4. ネットワーク カード IPv6 をオフにします。 5. WindowsUpdateTroubleshooter ツールを実行して修復します。 6. ファイアウォールをオフにします。およびその他の関連するウイルス対策ソフトウェア。 7. WidowsUpdate サービスを閉じます。解決策 3 解決策 4 Huawei コンピュータの Windows アップデート中に「0x8024401c」エラーが発生する 症状 問題 原因 解決策 まだ解決されませんか?最近、システムの脆弱性のため、Web サーバーを更新する必要があります。サーバーにログインすると、エラー コード 0x8024401c が表示されます。

新しい仮想マシンの作成中に、サーバーでエラー 0x80070003 が発生しました。 新しい仮想マシンの作成中に、サーバーでエラー 0x80070003 が発生しました。 Feb 19, 2024 pm 02:30 PM

Hyper-V を使用して仮想マシンを作成または起動するときにエラー コード 0x80070003 が発生した場合は、権限の問題、ファイルの破損、または構成エラーが原因である可能性があります。解決策には、ファイルのアクセス許可の確認、破損したファイルの修復、正しい構成の確認などが含まれます。この問題は、さまざまな可能性を 1 つずつ除外することで解決できます。エラー メッセージ全体は次のようになります。 [仮想マシン名] の作成中にサーバーでエラーが発生しました。新しい仮想マシンを作成できません。構成ストアにアクセスできません: システムは指定されたパスを見つけることができません。 (0x80070003)。このエラーの考えられる原因は次のとおりです。 仮想マシン ファイルが破損しています。これは、マルウェア、ウイルス、またはアドウェアの攻撃によって発生する可能性があります。このようなことが起こる可能性は低いですが、完全にそうなることはできません。

ネットワークが Wi-Fi に接続できない場合は何が起こっているのでしょうか? ネットワークが Wi-Fi に接続できない場合は何が起こっているのでしょうか? Apr 03, 2024 pm 12:11 PM

1. Wi-Fi パスワードを確認します。入力した Wi-Fi パスワードが正しいことを確認し、大文字と小文字の区別に注意してください。 2. Wi-Fi が適切に動作しているかどうかを確認する: Wi-Fi ルーターが正常に動作しているかどうかを確認し、同じルーターに他のデバイスを接続して、デバイスに問題があるかどうかを判断できます。 3. デバイスとルーターを再起動します。デバイスまたはルーターに誤動作やネットワークの問題が発生する場合があり、デバイスとルーターを再起動すると問題が解決する場合があります。 4. デバイスの設定を確認します。デバイスのワイヤレス機能がオンになっていて、Wi-Fi 機能が無効になっていないことを確認します。

パイオニアエラーコードKadena-Keeslerを修正 パイオニアエラーコードKadena-Keeslerを修正 Feb 19, 2024 pm 02:20 PM

Call of Duty: Vanguard のプレイ中に Kadena-Keesler エラーが発生した場合は、この記事が役立つ可能性があります。一部のプレイヤーからのフィードバックによると、このゲームは Windows PC、Xbox、PlayStation、その他のプラットフォームでこの問題が発生しているとのことです。このエラーが発生すると、次のエラー メッセージが表示される場合があります。 接続に失敗しました。ネットワーク接続に失敗しました。オンラインまたはローカル ネットワーク経由でプレイするには、アクティブなインターネット接続が必要です。 [理由: Kadena-Keesler] 次のエラー メッセージが表示される場合もあります。 接続に失敗しました オンライン サービスにアクセスできません。 [理由: Kadena-Keesler] Xbox でのこのエラーの別の例は次のとおりです: アクティブなネットワーク接続が必要です

See all articles