我有一个严重的问题。我有一个类来管理与NSOperationQueue的连接,并为该类委托实际为NSURLConnection委托方法的方法。有时,我必须弹出viewController,它具有我的委托类的答案,但它被困在委托函数的范围内,填充UITableView,应用程序就在那里崩溃。我在viewController dealloc和NSURLConnection委托上使用@synchronized解决了它,但我认为这是最简洁的方式,因为当我向tableView添加大量信息时,UI有时会冻结。那么,有没有办法干净呢?
- (void)viewDidLoad:(BOOL)animated {
[myManager startRequest];
}
//myManager class callback
- (void)managerDelegate {
//Doing things and just poped the viewController while in the function scope
//Program crashes, most of logs are "UIViewController message sent to deallocated instance"
}
//viewController dealloc
- (void)dealloc
{
@synchronized([Manager class])
{
alert.delegate = nil;
searchResultsTableView.delegate = nil;
searchResultsTableView.dataSource = nil;
[searchResultsTableView release]; searchResultsTableView = nil;
[serviceSearch release]; serviceSearch = nil;
[searchResults release]; searchResults = nil;
[XMLTag release]; XMLTag = nil;
[XMLParserServicesKeys release]; XMLParserServicesKeys = nil;
[XMLParserKeys release]; XMLParserKeys = nil;
[searchString release]; searchString = nil;
[__managedObjectContext release]; __managedObjectContext = nil;
manager.delegate = nil;
[manager stopAllRequests];
[manager release]; manager = nil;
[super dealloc];
}
}
编辑:更多代码,现在是myManager类
- (void) stopAllRequests
{
#ifdef _WSMANAGER_DEBUG_
NSLog(@"stopAllRequests %d", [connectionsArray count]);
#endif
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
for(NNSURLConnection* connection in connectionsArray)
{
[connection cancel];
[connection release];
}
[connectionsArray removeAllObjects];
[queue cancelAllOperations];
}
- (BOOL)startRequest
{
//Data initialization
NSInvocationOperation* operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(beginConnection:) object:[NSDictionary dictionaryWithObjectsAndKeys:request, kRequestKey, keyInfo, kUserInfoKey, nil]];
[queue addOperation:operation];
[operation release];
return YES;
}
-(void) beginConnection:(id)object
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NNSURLConnection* connection = [[NNSURLConnection alloc] initWithRequest:[object objectForKey:kRequestKey] delegate:self];
if(connection)
{
NSMutableData *requestData = [[NSMutableData alloc] init];
connection.url = [((NSURLRequest*)[object objectForKey:kRequestKey]) URL];
connection.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[object objectForKey:kUserInfoKey], kUserInfoKey, requestData, kRequestDataKey, nil];
[connectionsArray addObject:connection];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:TIMEOUT]];
if([connectionsArray indexOfObject:connection] != NSNotFound)
{
[connection cancel];
if([delegate conformsToProtocol:@protocol(ManagerDelegate)] && [delegate respondsToSelector:@selector(managerFailed:withKey:errorCode:)]) {
[delegate managerFailed:self withKey:[connection.userInfo objectForKey:kUserInfoKey] errorCode:ManagerErrorCodeTimeout];
if([connectionsArray count] < 1)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
[connectionsArray removeObject:connection];
}
[pool drain];
}
- (void)connectionDidFinishLoading:(NNSURLConnection *)connection {
@synchronized([Manager class])
{
NSMutableData *requestData = [connection.userInfo objectForKey:kRequestDataKey];
NSString* responseString = [[NSString alloc] initWithData:requestData encoding:NSUTF8StringEncoding];
if([delegate conformsToProtocol:@protocol(PLMWSManagerDelegate)] && [delegate respondsToSelector:@selector(managerSuccess:responseString:withKey:)])
[delegate managerSuccess:self responseString:responseString withKey:[connection.userInfo objectForKey:kUserInfoKey]];
[responseString release];
[requestData release];
[connectionsArray removeObject:connection];
[connection cancel];
[connection release]; connection = nil;
if([connectionsArray count] < 1)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
答案 0 :(得分:1)
当应用程序从INTERNET获取数据以填充GUI时,保持GUI和INTERNET线程完全分离将有助于完整。在相同的情况下,以下选项对我有效。
使用NSOperationQueue的– cancelAllOperations
方法。这将取消目前正在运行的操作后的问题。如果您不取消操作,那么它将在后台运行。
在表和INTERNET线程之间保持桥数据结构。将它保存在其他类中,然后保存在viewController中,以便在弹出viewcontroller时不会释放它。
从该数据结构填充表并填充其中的新可用数据。当新数据可用时,使用NSNotification或其他方法通知表。所以在这种情况下,如果你让你在后台运行的操作队列,它将不会更新tableView。
编辑:
此外,在使用NSURLconnection时,可以使用- (void)cancel
方法取消连接。取消连接后,代理将不再接听电话。它有两种方式有用。
取消上一次操作。
在您调用自定义委托子项的类中实现类似这样的内容。
希望以上方法有所帮助。
答案 1 :(得分:1)
标准开场评论,突出了未来:Apple对LLVM项目的贡献表明iOS 5将带有自我归零的弱引用。因此,对象可以在不拥有它们的情况下保存对其他对象的引用,并且当这些对象被释放时,所有指向它们的指针都被神奇地设置为nil
。我们将不得不等待开发人员工具的公开发布,以确定iOS是否实现了该功能(由于显而易见的原因需要一些运行时支持),如果是这样,Apple是否将其用于代理。但是,这很可能很快,作为开发人员,您应该能够获得当前可用的工具的NDA版本,因此根据项目的其他实际情况,可能需要考虑到解决方案的路径。 / p>
对现在和现在更有帮助:
NSURLConnection
的委托是不可变的。假设NSURLConnection
是由你提到的NSOperationQueue
上的任务创建的,你也可能在一些runloop上创建它们而不是附加到主线程的那些,从而将整个线程安全问题带入混合。
我建议聪明的事情是:
NSURLConnection
附加到主runloop。为此,您应该创建连接,以便它们不会立即启动,使用scheduleInRunLoop:forMode:
提名[NSRunLoop mainRunLoop]
(可能NSDefaultRunLoopMode
),然后启动它们。@synchronized
块将其添加到合适的NSMutableArray
waitUntilAllOperationsAreFinished
,然后将cancel
发送到所有正在进行的连接(例如[self.arrayOfConnections makeObjectsPerformSelector:@selector(cancel)]
)保证连接在收到cancel
消息后不与其代理通信。您希望等到队列中的所有操作都完成,以避免潜在的竞争条件,即视图控制器已取消分配,但在某个相关时间成功终止了所有连接,但尚未完成的操作则尝试添加新的连接。 / p>
另外,根据我们对该问题的评论讨论:
NSURLConnection有一个内置系统,可以在运行循环中异步运行。运行循环是Apple的事件循环版本,粗略地说是要发布的消息列表。所以他们让你使用一个线程做很多事情,但前提是没有一件东西阻止线程。
Apple实际上建议通过允许NSURLConnection在主线程上异步运行来实现异步URL访问的最有效方式(在处理和电池寿命方面)。您可以将当前-(void) startConnection
调整为:
- (BOOL)startRequest
{
// we're going to do all this right here on the main thread, so there's
// no need to package 'request' and 'keyInfo' into a dictionary and create
// an operation
// create the connection
NNSURLConnection* connection = [[NNSURLConnection alloc] initWithRequest:request delegate:self];
if(connection)
{
NSMutableData *requestData = [[NSMutableData alloc] init];
connection.url = [keyInfo URL];
connection.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyInfo, kUserInfoKey, requestData, kRequestDataKey, nil];
[connectionsArray addObject:connection];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
// We used initWithRequest:delegate: so the request has already started.
// The connection will now run asynchronously and we can wait for
// delegate messages, which will be delivered via the runloop on this thread
}
然后在连接结束后将您正在做的其他事情放入connectionDidFinishLoading:
:
- (void)connectionDidFinishLoading:(NNSURLConnection *)connection {
if([connectionsArray indexOfObject:connection] != NSNotFound)
{
[connection cancel];
if([delegate conformsToProtocol:@protocol(ManagerDelegate)] && [delegate respondsToSelector:@selector(managerFailed:withKey:errorCode:)]) {
[delegate managerFailed:self withKey:[connection.userInfo objectForKey:kUserInfoKey] errorCode:ManagerErrorCodeTimeout];
if([connectionsArray count] < 1)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
[connectionsArray removeObject:connection];
}
/* ...and all the other stuff here... */
}
我一直认为NNSURLConnection
是NSURLConnection
的子类,只是添加了一些额外的属性,而不会影响行为。
答案 2 :(得分:0)
为什么你这样做是有很好的理由,但你为什么不使用第三方库ASIHttpConnection?
它管理URL连接细节,甚至还带有内置的操作队列。
还有一点值得注意,正如Tommy Obliquely指出ARC即将上市(ARC已经公开宣布,因此可以提及它存在)。由于ARC支持4.0设备,如果可能的话,移动到那将是一个非常好的主意。你可能遇到一些问题,但它可能会解决比它造成的问题更多的问题。