2013-10-23 40 views
6

Tôi đã tìm thấy sự cố có vẻ như gây ra bế tắc trong WebKit. Nếu tôi chạy mã này từ chủ đề chính của mình, tôi sẽ thấy một cảnh báo. Tôi có thể gõ vào nút "OK" trên báo và nó bác bỏ và tất cả đang làm việc tốt:Bế tắc với GCD và webView

[theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; 

Nếu tôi làm cho một sửa đổi nhỏ, sau đó các thông điệp cảnh báo vẫn xuất hiện, nhưng nút OK không thể được khai thác trên - bạn không thể bỏ qua những cảnh báo và nếu bạn đột nhập vào ứng dụng nó được treo ở stringByEvaluatingJavaScriptFromString gọi:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; 
    }); 
}); 

các chỉ khác nhau trong hai là trong một giây, nó đang chạy JS trong chủ đề chính trong ngữ cảnh của một hàng đợi công văn.

Mặt khác, nếu tôi làm như sau, sau đó được hang không xảy ra:

- (void) showHi:(id) it 
{ 
    [(UIWebView*)it stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; 
} 

.... 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO]; 
}); 

Ai đó có thể tỏa sáng một số ánh sáng về những gì đang xảy ra sai gây ra treo?

EDIT:

câu hỏi liên quan:

Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?
Whats the difference between performSelectorOnMainThread and dispatch_async on main queue?
Grand Central Dispatch (GCD) vs. performSelector - need a better explanation

câu hỏi Rất tương tự:

UIWebView stringByEvaluatingJavaScriptFromString hangs on iOS5.0/5.1 when called using GCD

Trả lời

4

Điều này có vẻ đơn giản là một lỗi trong UIWebView. Theo số this question, nó đã được giới thiệu trong iOS 5 và đã làm không phải là bế tắc trên iOS 4.3 trở xuống.

Điều thú vị là, trình bày một UIAlertView ngay trước khi cuộc gọi đến stringByEvaluatingJavaScriptFromString: lạ ngăn ngừa sự bế tắc:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     UIAlertView *message = [[UIAlertView alloc] initWithTitle:@"Test" 
                  message:@"Test" 
                 delegate:nil 
               cancelButtonTitle:@"OK" 
               otherButtonTitles:nil]; 
     [message show]; 

     [theWebView stringByEvaluatingJavaScriptFromString:@"alert('hi');"]; 
    }); 
}); 

Tuy nhiên, lý thuyết của tôi là thế này: Khi tôi tạm dừng thực hiện sau khi bế tắc xảy ra, tôi thấy rằng WebThread phải dừng lại tại __psynch_mutexwait. Vì công cụ JavaScript được thực hiện trên các luồng khác nhau, nó phải cho biết chuỗi chính để hiển thị khung nhìn cảnh báo. Tuy nhiên, stringByEvaluatingJavaScriptFromString: là cuộc gọi chặn trả về một giá trị. Giá trị chỉ có thể được trả lại khi cảnh báo được loại bỏ bằng cách nhấp vào OK. Đây là nơi mà bế tắc dường như xảy ra: Từ một chủ đề khác, chúng ta nói với chủ đề chính để nói xem web chạy JavaScript (điều này xảy ra trên một chủ đề khác), điều này cho biết chủ đề chính hiển thị một khung nhìn cảnh báo. cung cấp giá trị trả lại của nó quay lại JavaScript khi OK được nhấp. Và chỉ khi cuộc gọi trả về stringByEvaluatingJavaScriptFromString: là khối mà chúng tôi đã chuyển đến GCD hoàn chỉnh.

Tuy nhiên, đó phải là lỗi. Thật lạ khi bế tắc không xảy ra khi tôi hiển thị UIAlertView trước. Có thể iOS đặt chế độ xem cảnh báo thứ hai trên một số loại hàng đợi trong trường hợp đó, ngăn chặn bế tắc. Odd!

5

tôi nghĩ rằng nó đã được ghi trong tài liệu tham khảo webviewImage lớp

Bây giờ, Đối với trường hợp của bạn của khóa chết bạn có thể mô phỏng tương tự bằng cách thay thế này [self performSelectorOnMainThread:@selector(showHi:) withObject:theWebView waitUntilDone:NO];

với

performSelector:withObject:afterDelay:inModes:. Với chế độ Phản hồi theo mặc định, nó là NSDefaultRunLoopMode và đó là nguyên tử trong khi trong trường hợp nonatomic của bạn là dispatch_get_main_queue(), bạn phải thay đổi chế độ gửi luồng hiện tại.

Tôi hy vọng nó vẫn mang tính thông tin. Ngoài ra, nhiều đề xuất được hoan nghênh.

+0

Bạn có thể giải thích thêm về các chế độ vòng lặp chạy hay không và cách thực thi 'dispatch_async' sẽ khiến mã chạy trong chuỗi chính? Ngoài ra, làm thế nào bạn có thể thay đổi chế độ gửi luồng? –

+0

@itechnician - tôi đang đối mặt với cùng một vấn đề.Error là void SendDelegateMessage (NSInvocation *): delegate (webView: identifierForInitialRequest: fromDataSource :) không quay trở lại sau khi chờ 10 giây. chế độ vòng lặp chạy chính: kCFRunLoopDefaultMode và nó đang chặn giao diện người dùng. WebView đang đóng băng. Vì vậy, bạn có bất kỳ ý tưởng nào là cách tốt nhất để giải quyết vấn đề này. – Sandeep

0

Có một số điều có thể là một yếu tố trong việc này. Như những người khác đã đề cập, JavaScript không thực hiện trên chủ đề chính, mà là chủ đề nền của riêng nó. Bạn có thể nhận thấy rằng việc thực thi mã của bạn chờ cho đến khi JavaScript hoàn tất. Tôi không biết triển khai cụ thể trong mã, nhưng có vẻ như nó đang sử dụng nội bộ bằng cách sử dụng dispatch_sync hoặc dispatch_barrier_sync.

Mục tiếp theo mà bạn cần lưu ý là có sự khác biệt đáng kể giữa các cảnh báo được trình bày từ UIAlertView -show và JavaScript alert(..). UIAlertView -show hoạt động không đồng bộ. Bạn có thể nói điều này vì việc thực thi mã vẫn tiếp tục sau khi cuộc gọi đến -show trước khi cảnh báo được loại bỏ. Nếu bạn thực hiện thử nghiệm tương tự này trong JavaScript, việc thực thi mã sẽ bị ngừng cho đến khi cảnh báo được đóng lại.

Cuối cùng, có sự khác biệt giữa hàng đợi và chuỗi gửi. Bạn có thể đọc thêm trong số this thread về nó. Tôi nghi ngờ việc thực thi JavaScript đang thực hiện [NSThread isMainThread] và đang báo cáo NO vì nó nằm trên hàng đợi chính, nhưng không phải là chuỗi chính. Vì séc này đang báo cáo NO, nó thực hiện dispatch_sync vào hàng đợi chính, đã bị chặn chờ hàng đợi JavaScript.

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