在混合Swift Objective-C环境中调用NSURLConnection时没有响应

时间:2014-12-30 14:53:20

标签: ios objective-c swift nsurlconnection

我已经创建了类StartConnection来处理我的NSURL请求。它从我的AppDelegate调用两次,效果很好。它也被另一个类调用,并且也很有效。这是StartConnection的实现:

#import "StartConnection.h"
#import "BlaBlaBlog-swift.h"

@implementation StartConnection
{
    BOOL startedForBlog;
}

- (void)getRssFileWithUrl: (NSString*)rssUrlString forBlog:(BOOL)forBlog
{
    startedForBlog = forBlog;
    NSURL *url = [NSURL URLWithString:rssUrlString];
    NSURLRequest *rssRequest = [NSURLRequest requestWithURL:url];
    NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:rssRequest delegate:self];
    [connection start];
}

-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    dataSize = [response expectedContentLength];
}


- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if (receivedData==nil )
    {
        receivedData = [[NSMutableData alloc]init];
    }
    // Append the new data to the instance variable you declared
    [receivedData appendData:data];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    self.receivedDataComplete = receivedData;
    if (startedForBlog){
        [self.delegate performSelector: @selector(receiveDataCompleted)];
    }
    else
        [self.delegate performSelector: @selector(receivePodCastDataCompleted)];
}

为了获得SWIFT的经验,我在我的代码中添加了一个实验SWIFT类,它也使用了StartConnection.h。在调试器中,我可以看到正在创建一个StartConnection实例,并且getFileWithUrl方法似乎正常被踢掉了。但就是这一切。没有调用任何委托方法。

这是SWIFT课程:

import UIKit

class CheckActuality: NSObject, GetReceivedDataProtocol, WordPressParserDelegate {

    var retrievePostData = StartConnection()
    var parseCompleted: Bool=false
    var result: Bool = true
    lazy var wpParser = WordPressParser()
    lazy var defaults = NSUserDefaults.standardUserDefaults()


    func isActual () -> Bool {
        var url = "http://blablablog.nl/new_api.php?function=get_recent_posts&count=1"
        self.retrievePostData.delegate=self
        self.retrievePostData.getRssFileWithUrl(url, forBlog:true)
        while !self.parseCompleted
        {
//           wait till wpparser has completed
        }
        if self.wpParser.arrayWithPostDictionaries.count == 1
//            Some info has been retrieved
        {
            var posts: NSArray = wpParser.arrayWithPostDictionaries
            var post: NSDictionary = posts.objectAtIndex(0) as NSDictionary
            var latestPostUrl: String = post.objectForKey("postUrl") as String
            var currentLatestPostUrl = defaults.stringForKey("ttt")

            if latestPostUrl != currentLatestPostUrl {
              result = false
            }
            else {
                result = true
            }
        }
        return result
    }


    func receiveDataCompleted () {
        if self.retrievePostData.receivedDataComplete != nil
        {
            self.wpParser.delegate=self
            self.wpParser.parseData(retrievePostData.receivedDataComplete)
        }
        else
        {
//            warning no internet
        }

    }

    func wpParseCompleted () {
        self.parseCompleted=true
    }

}

要完成,我的AppDelegate中的调用如下所示:

//
//    retrieving PostData. Create a connection, set delegate to self en start with created url
//
    retrievePostData = [[StartConnection alloc]init];
    retrievePostData.delegate = self;
    NSString *url = [[wordPressUrl stringByAppendingString:apiString] stringByAppendingString:pageCountString];
    [retrievePostData getRssFileWithUrl:url forBlog:(BOOL)true];
//
//    retrieving PodcastData. Create a connection, set delegate to self en start with created url
//
    retrievePodCastData = [[StartConnection alloc]init];
    retrievePodCastData.delegate = self;
    [retrievePodCastData getRssFileWithUrl:podcastUrl forBlog:(BOOL)false];

我几乎一天都不知所措。希望你们中的一些经验丰富的人可以帮助这个初学者。

1 个答案:

答案 0 :(得分:1)

while !parseCompleted循环阻塞主线程,直到下载和解析完成。但是接收数据的处理也发生在主线程上,所以如果该线程被阻止,你的应用就会陷入僵局。

我会消除while循环并将所有后期处理放在receivedDataComplete方法中。


顺便说一句,这种变化隐含的事实是isActual必须是异步方法。因此,不应返回Bool值,而应该是Void返回类型,但您应该使用completionHandler模式(并且我将其更改为也返回错误对象,也是):

class CheckActuality: NSObject, GetReceivedDataProtocol, WordPressParserDelegate {

    let errorDomain = "com.domain.app.CheckActuality"

    lazy var retrievePostData = StartConnection()
    lazy var wpParser = WordPressParser()
    lazy var defaults = NSUserDefaults.standardUserDefaults()

    var completionHandler: ((latestPost: Bool!, error: NSError?)->())!

    func isActual(completionHandler: (latestPost: Bool!, error: NSError?)->()) {
        // save the completionHandler, which will be called by `receiveDataCompleted` or `wpParseCompleted`

        self.completionHandler = completionHandler

        // now perform query

        let url = "http://blablablog.nl/new_api.php?function=get_recent_posts&count=1"  // as an aside, use `let` here
        retrievePostData.delegate = self                                                // also note that use of `self` is redundant here
        retrievePostData.getRssFileWithUrl(url, forBlog:true)
    }    

    func receiveDataCompleted () {
        if retrievePostData.receivedDataComplete != nil {
            wpParser.delegate = self
            wpParser.parseData(retrievePostData.receivedDataComplete)
        } else {
            // frankly, I'd rather see you change this `receiveDataCompleted` return the `NSError` from the connection, but in the absence of that, let's send our own error
            let error = NSError(domain: errorDomain, code: 1, userInfo: nil)
            completionHandler(latestPost: nil, error: error)
        }
    }

    func wpParseCompleted () {
        if wpParser.arrayWithPostDictionaries.count == 1 {                     // Some info has been retrieved
            let posts: NSArray = wpParser.arrayWithPostDictionaries
            let post: NSDictionary = posts.objectAtIndex(0) as NSDictionary
            let latestPost: String = post.objectForKey("postUrl") as String
            let currentlatestPost = defaults.stringForKey("ttt")

            completionHandler(latestPost: (latestPost != currentlatestPost), error: nil)
        }

        // again, I'd rather see you return a meaningful error returned by the WordPressParser, but I'll make up an error object for now
        let error = NSError(domain: errorDomain, code: 2, userInfo: nil)
        completionHandler(latestPost: nil, error: error)
    }

}

现在,我不知道latestPost是否是您尝试返回的值的合适名称,因此将其更改为对您的例程有意义的任何内容。此外,名称isActual并不真正有意义,但我会让您将其更改为您想要的任何内容。

无论如何,当您使用它时,您将使用尾随闭包语法来指定将异步执行的completionHandler块:

let checkActuality = CheckActuality()

func someFunc() {
    checkActuality.isActual() { latestPost, error in 
        if error != nil {
            // do whatever error handling you want

            println(error) 
        } else if latestPost {
            // yes, latest post
        } else {
            // nope
        }
    }

    // note, do not try to check `latestPost` here because the 
    // above closure runs asynchronously
}

毋庸置疑,这是completionHandler模式,但在查看代码时,您似乎更喜欢delegate模式。如果您想使用delegate模式实现此功能,则可以。但是这个想法是一样的:这个isActual方法(无论你最终将它重命名为)都是异步运行的,所以你必须在调用者完成时通知它。