2010-01-24 35 views
5

Tôi đang cố gắng tạo lớp sẽ xử lý nhiều lượt tải xuống cùng một lúc (tôi cần tải xuống nhiều tệp nhỏ) và tôi gặp sự cố với kết nối "biến mất".tải xuống nền đồng thời trên iphone

Tôi có chức năng addDonwload để thêm url vào danh sách các url cần tải xuống và kiểm tra xem có sẵn nơi tải xuống miễn phí hay không. Nếu có nó bắt đầu tải xuống ngay lập tức. Khi một trong các tải xuống hoàn tất, tôi chọn danh sách biểu mẫu url đầu tiên và bắt đầu tải xuống mới.

tôi sử dụng NSURLConnection để tải về, đây là một số mã

- (bool) TryDownload:(downloadInfo*)info 
{ 
    int index; 
    @synchronized(_asyncConnection) 
    { 
     index = [_asyncConnection indexOfObject:nullObject]; 
     if(index != NSNotFound) 
     { 
      NSLog(@"downloading %@ at index %i", info.url, index); 
      activeInfo[index] = info; 
      NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15]; 

      [_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]]; 
      //[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];   

      return true; 
     } 
    } 

    return false; 
} 

- (void)connectionDidFinishLoading:(NSURLConnection*)connection 
{ 
    [self performSelectorOnMainThread:@selector(DownloadFinished:) withObject:connection waitUntilDone:false]; 
} 

- (void)DownloadFinished:(id)connection 
{ 
    NSInteger index = NSNotFound; 
    @synchronized(_asyncConnection) 
    { 
     index = [_asyncConnection indexOfObject:(NSURLConnection*)connection]; 
    } 

    [(id)activeInfo[index].delegate performSelectorInBackground:@selector(backgroundDownloadSucceededWithData:) withObject:_data[index]]; 
    [_data[index] release]; 
    [activeInfo[index].delegate release]; 
    @synchronized(_asyncConnection) 
    { 
     [[_asyncConnection objectAtIndex:index] release]; 
     [_asyncConnection replaceObjectAtIndex:index withObject:nullObject];    
    } 
    @synchronized(downloadQueue) 
    { 
     [downloadQueue removeObject:activeInfo[index]]; 
     [self NextDownload]; 
    } 
} 

- (void)NextDownload 
{ 
    NSLog(@"files remaining: %i", downloadQueue.count); 
    if(downloadQueue.count > 0) 
    { 
     if([self TryDownload:[downloadQueue objectAtIndex:0]]) 
     { 
      [downloadQueue removeObjectAtIndex:0]; 
     } 
    } 
} 

_asyncConnection là mảng của tôi về khe tải (NSURLConnections) downloadQueue là danh sách các url tải

gì xảy ra được, ngay từ đầu mọi thứ hoạt động tốt, nhưng sau khi tải xuống một vài kết nối của tôi bắt đầu biến mất. Quá trình tải xuống bắt đầu nhưng kết nối: didReceiveResponse: không bao giờ được gọi. Có một điều trong giao diện điều khiển đầu ra mà tôi không hiểu tôi có thể giúp một chút. Normaly có một cái gì đó như 2010-01-24 21: 44: 17.504 appName [3057: 207] trước thông điệp NSLog của tôi. Tôi đoán rằng số trong dấu ngoặc vuông là một số loại ứng dụng: id chủ đề? tất cả mọi thứ hoạt động ok trong khi có cùng một số, nhưng sau một thời gian, "NSLog (@" tải xuống% @ tại chỉ mục% i ", info.url, index);" tin nhắn bắt đầu có số thứ hai khác nhau. Và khi điều đó xảy ra, tôi sẽ ngừng nhận bất kỳ cuộc gọi lại nào cho việc kết nối url đó.

Điều này đã khiến tôi phát điên vì tôi có thời hạn nghiêm ngặt và tôi không thể tìm thấy sự cố. Tôi không có nhiều kinh nghiệm với iphone dev và các ứng dụng đa luồng. Tôi đã thử các cách tiếp cận khác nhau nên mã của tôi khá lộn xộn, nhưng tôi hy vọng bạn sẽ thấy những gì tôi đang cố gắng làm ở đây :)

btw là bất kỳ ai trong các bạn biết về lớp/lib hiện có mà tôi có thể sử dụng cũng. Tôi muốn tải xuống song song với khả năng o tự động thêm tệp mới để tải xuống (do đó, khởi chạy trình tải xuống ngay từ đầu với tất cả các url không hữu ích cho tôi)

Trả lời

2

Bạn có một loạt các sự cố nghiêm trọng về bộ nhớ và các sự cố đồng bộ hóa chuỗi trong mã này.

Thay vì đi sâu vào tất cả, tôi sẽ hỏi câu hỏi sau: Bạn đang làm điều này trên một chuỗi nền của một số loại? Tại sao? IIRC NSURLConnection đã thực hiện tải xuống của nó trên một chuỗi nền và gọi đại biểu của bạn trên chuỗi mà NSURLConnection đã được tạo ra (ví dụ: chủ đề chính của bạn là lý tưởng).

Đề nghị bạn quay trở lại, đọc lại tài liệu NSURLConnection và sau đó xóa mã luồng nền của bạn và tất cả sự phức tạp mà bạn đã tiêm vào không cần thiết này.

Đề xuất thêm: Thay vì cố gắng duy trì vị trí song song trong hai mảng (và một số mã sơ sài ở trên liên quan đến điều đó), tạo một mảng và có đối tượng chứa cả NSURLConnection VÀ đối tượng biểu thị kết quả. Sau đó, bạn chỉ có thể phát hành var instance kết nối khi kết nối được thực hiện. Và đối tượng cha (và do đó dữ liệu) khi bạn làm xong dữ liệu.

+0

cảm ơn lời khuyên của bạn. Tôi hoàn toàn viết lại lớp của mình và nó hoạt động như mong đợi. Xin lỗi vì sự chậm trễ nhưng tôi hoàn toàn quên rằng điều này đã được giải quyết từ lâu rồi :) – Lope

0

Đoạn mã này có thể là nguồn của lỗi, bạn giải phóng đối tượng được trỏ tới bởi con trỏ activeInfo[index].delegate ngay sau khi thực hiện cuộc gọi phương thức không đồng bộ trên đối tượng đó.

[(id)activeInfo[index].delegate performSelectorInBackground:@selector(backgroundDownloadSucceededWithData:) withObject:_data[index]]; 
[_data[index] release]; 
[activeInfo[index].delegate release]; 
+0

trình tải xuống này ban đầu được tuần tự và nó hoạt động rất tốt, phần mã này đến từ phần chức năng trước đây. Tôi nghĩ rằng performSelector giữ lại đối tượng mà tôi gửi làm tham số. Dù sao, ngay cả khi tôi nhận xét nó ra, nó không thay đổi bất cứ điều gì – Lope

0

Bạn có sử dụng connection:didFailWithError: không? Có thể có một khoảng thời gian chờ ngăn không cho quá trình tải xuống thành công.

Cố gắng loại bỏ các khối @synchronized và xem điều gì sẽ xảy ra.

Chuỗi bên trong dấu ngoặc vuông có vẻ là số nhận dạng chuỗi như bạn đã đoán. Vì vậy, có thể bạn bị khóa trong số @synchronized. Trên thực tế, tôi không thấy lý do chuyển đổi chuỗi - tất cả mã có vấn đề sẽ chạy trong chủ đề chính (performSelectorOnMainThread) ...

Nhưng dù sao, không cần sử dụng cả @synchronizedperformSelectorOnMainThread.

BTW, tôi không thấy dòng NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];. Bạn bắt đầu kết nối ở đâu?

Đối với các bản tải xuống song song - Tôi nghĩ rằng bạn có thể tải xuống nhiều tệp cùng một lúc với cùng mã mà bạn sử dụng tại đây. Chỉ cần tạo một kết nối riêng biệt cho mỗi lần tải xuống.

+0

Kết nối được bắt đầu trong chức năng TryDownload ([_asyncConnection replaceObjectAtIndex: index withObject: [[NSURLConnection alloc] initWithRequest: yêu cầu delegate: self startImmediately: TRUE]]; ) Tôi sử dụng didFailWithError, nhưng nó là tương tự để tải xuống thành công và nó không được gọi khi tôi mất một trong các kết nối, vì vậy tôi đã xóa nó (tôi đã đề cập đến điều đó, xin lỗi). Khi tôi xóa @không đồng bộ hóa khối, tôi đã nhận được "thay đổi trong khi liệt kê" lỗi ngay cả khi tôi sử dụng performSelectorOnMainThread. Tôi đã thử cả hai phương pháp, nhưng không ai trong số họ làm việc – Lope

+0

Hãy thử sử dụng NSNotificationCenter để bắt đầu tải xuống mới thay vì performSelectorOnMainThread ... –

0

Cân nhắc chỉ giữ một hàng đợi tải xuống cùng với số lượng kết nối đang hoạt động, popping mục ra khỏi hàng đợi khi tải xuống hoàn tất và một vị trí sẽ trở nên miễn phí. Sau đó bạn có thể kích hoạt các đối tượng NSURLConnection một cách không đồng bộ và xử lý các sự kiện trên luồng chính.

Nếu bạn thấy rằng cách tiếp cận song song của bạn cấm thực hiện tất cả quá trình xử lý trên chuỗi chính, hãy xem xét có đối tượng người quản lý trung gian giữa mã tải xuống luồng chính và NSURLConnection. Sử dụng cách tiếp cận đó, bạn sẽ khởi tạo trình quản lý của mình và sử dụng đồng thời NSURLConnection trên một chuỗi nền. Người quản lý đó sau đó hoàn toàn giao dịch với việc tải xuống và chuyển kết quả trả về cho đại biểu chủ đề chính của nó bằng cách sử dụng lệnh performSelectorOnMainThread: withObject:. Sau đó, mỗi lần tải xuống chỉ là trường hợp tạo đối tượng người quản lý mới khi bạn có một vị trí miễn phí và cài đặt nó.

1

tôi khuyên bạn hãy xem này: http://allseeing-i.com/ASIHTTPRequest/

Đó là một tập khá phức tạp của các lớp học với điều khoản cấp phép tự do (free quá).

Nó có thể cung cấp nhiều chức năng mà bạn muốn.

Các vấn đề liên quan