2010-04-25 44 views
9

Tôi đang cố gắng lấy dữ liệu từ trang web-xml. Mọi thứ đều hoạt động tốt.Cuộc gọi không đồng bộ Mục tiêu C iphone

Nhưng UIButton vẫn được nhấn cho đến khi dữ liệu xml được trả về và do đó nếu có vấn đề với dịch vụ internet, nó không thể sửa chữa được và ứng dụng hầu như không sử dụng được.

đây là những cuộc gọi:

{ 
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    if(!appDelegate.XMLdataArray.count > 0){ 
     [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; 
     [appDelegate GetApps]; //function that retrieves data from Website and puts into the array - XMLdataArray. 

    } 
    XMLViewController *controller = [[XMLViewController alloc] initWithNibName:@"MedGearsApps" bundle:nil]; 
    [self.navigationController pushViewController:controller animated:YES]; 
    [controller release]; 
} 

Nó hoạt động tốt, nhưng làm thế nào tôi có thể làm cho các nút xem chức năng với bị mắc kẹt. Nói cách khác, tôi chỉ muốn UIButton và các UIButton khác là các hàm chức năng hoạt động ở chế độ nền.

Tôi nghe nói về performSelectorInMainThread nhưng tôi không thể đặt nó để thực hành một cách chính xác

bất kỳ help is appreciated :)

Trả lời

36

Bạn không hiểu mô hình luồng nhiều và có thể bạn sẽ tự bắn mình vào chân nếu bạn bắt đầu thêm mã không đồng bộ mà không thực sự hiểu những gì đang diễn ra.

Mã bạn đã viết chạy trong chuỗi ứng dụng chính. Nhưng khi bạn nghĩ về nó, bạn không phải viết hàm main - bạn chỉ cần thực hiện ủy nhiệm ứng dụng và gọi lại sự kiện (chẳng hạn như trình xử lý cảm ứng) và bằng cách nào đó chúng chạy tự động khi thời gian đến. Đây không phải là một phép thuật, đây chỉ đơn giản là một đối tượng Cocoa gọi là Run Loop.

Chạy vòng lặp là đối tượng nhận tất cả các sự kiện, quá trình hẹn giờ (như trong NSTimer) và chạy mã của bạn. Điều đó có nghĩa rằng khi bạn, ví dụ, làm điều gì đó khi người dùng chạm vào một nút, cây gọi trông hơi như thế này:

main thread running 
    main run loop 
     // fire timers 
     // receive events — aha, here we have an event, let’s call the handler 
     view::touchesBegan… 
      // use tapped some button, let’s fire the callback 
      someButton::touchUpInside 
       yourCode 

Bây giờ yourCode làm những gì bạn muốn làm và Run Vòng tiếp tục chạy. Nhưng khi mã của bạn mất quá nhiều thời gian để hoàn thành, chẳng hạn như trong trường hợp của bạn, Run Loop phải đợi và do đó các sự kiện sẽ không được xử lý cho đến khi mã của bạn kết thúc. Đây là những gì bạn thấy trong ứng dụng của bạn.

Để giải quyết tình huống, bạn phải chạy thao tác dài trong một chuỗi khác. Điều này không phải là rất khó, nhưng bạn sẽ phải suy nghĩ về một vài vấn đề tiềm năng.Chạy trong chủ đề khác có thể dễ dàng như gọi performSelectorInBackground:

[appDelegate performSelectorInBackground:@selector(GetApps) withObject:nil]; 

Và bây giờ bạn phải nghĩ ra một cách để nói với ứng dụng dữ liệu đã được nạp, chẳng hạn như sử dụng một thông báo hoặc gọi một selector trên chính chủ đề. Bằng cách này: lưu trữ dữ liệu trong ứng dụng đại biểu (hoặc thậm chí sử dụng ứng dụng đại biểu để tải dữ liệu) không phải là giải pháp rất thanh lịch, nhưng đó là một câu chuyện khác.

Nếu bạn chọn giải pháp performSelectorInBackground, hãy xem câu hỏi liên quan về memory management in secondary threads. Bạn sẽ cần hồ bơi tự động của riêng mình để bạn không bị rò rỉ các đối tượng tự động phát hiện.


Đang cập nhật câu trả lời sau một thời gian - ngày nay nó thường là tốt nhất để chạy mã trong nền sử dụng Grand Central Dispatch:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    // No explicit autorelease pool needed here. 
    // The code runs in background, not strangling 
    // the main run loop. 
    [self doSomeLongOperation]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 
     // This will be called on the main thread, so that 
     // you can update the UI, for example. 
     [self longOperationDone]; 
    }); 
}); 
+0

Yea, tôi đã đọc lên những điều này, nhưng vẫn còn rất nhiều điều để hiểu, nhưng tôi sẽ hiểu, bạn đã đưa tôi đi đúng hướng. cảm ơn. Tôi đã sử dụng thành phần TBXML, vì vậy, tôi đoán tôi cần phải viết lên một cách đơn giản để hiểu cách nó hoạt động. anyway, Một câu hỏi khác có liên quan là, nếu người dùng chọn đóng chế độ xem, có cách nào để chấm dứt cuộc gọi selectorInBackground không? trên ViewDidUnload ..? – Sam

+1

Điều đó phụ thuộc. Trong các trường hợp đơn giản, bạn chỉ có thể bỏ qua vấn đề và mã phần thông báo để nó không bị ngắt khi chế độ xem bị loại bỏ sớm. Một giải pháp chống đạn hơn có thể được viết bằng cách sử dụng chế độ 'NSURLConnection' không đồng bộ hoặc hàng đợi hoạt động, cả hai đều có một phương thức để hủy bỏ hoạt động đang tiến hành. – zoul

+0

Đoạn mã cuối cùng thật tuyệt vời, đó chính là điều tôi đang tìm kiếm. Cảm ơn nhiều! – kisileno

0

Bạn có thể tận dụng một hoạt động nền mà bị đẩy vào hàng đợi hoạt động:

BGOperation *op = [[BGOperation alloc] init]; 
[[self operationQueue] addOperation:op]; 
[op release]; 

Tôi đã tạo "các lệnh" cụ thể được thực thi trong nền:

@implementation BGOperation 

# pragma mark Memory Management 

- (BGOperation *)init 
{ 
if ((self = [super init]) != nil) 
    /* nothing */; 
return self; 
} 

- (void)dealloc 
{ 
self.jobId = nil; 
[super dealloc]; 
} 

# pragma mark - 
# pragma mark Background Operation 

- (void)main 
{ 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    [appDelegate GetApps]; 
[pool release]; 
return; 
} 

@end

Sau khi hoàn thành, có thể là một ý tưởng tốt để gửi thông báo đến chuỗi chính vì cơ sở dữ liệu nội bộ đã bị thay đổi.

+1

Không phải là nó dễ dàng hơn để sử dụng 'performSelectorInBackground' hoặc không đồng bộ ' Chế độ NSURLConnection'? – zoul

+1

Không, bởi vì sau đó bạn phải xử lý vòng đời của luồng (trong ví dụ đã cho, anh ta không cần phải tạo ra nhóm tự động của riêng mình). Asynch NSUrlConnection là OK nhưng nó hoạt động trên chủ đề chính và vì vậy có thể làm cho giao diện người dùng của bạn nói lắp, đặc biệt nếu bạn có một vài trong số chúng đi cùng một lúc. OperationQueues là xa, dễ dàng hơn để làm việc với và quản lý ... –

+0

Tạo và thoát một nhóm autorelease là hai dòng mã, nó không có vẻ như là một việc lớn đối với tôi. (Chỉ cần một quan điểm.) – zoul

0

Dường như bạn có thể đang sử dụng NSURLConnection bên trong phương thức getApps của mình. Nếu vậy, bạn nên chuyển đổi nó thành một cuộc gọi không đồng bộ.

3

Sử dụng phương pháp connectionWithRequest:delegate: của NSURLConnection. Điều này sẽ làm cho yêu cầu được chỉ định được gửi không đồng bộ. Đại biểu phải trả lời kết nối: didReceiveResponse: và sẽ được gửi tin nhắn đó khi nhận được phản hồi hoàn toàn.

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