我有一個奇怪的問題,它似乎陷入了一個似乎是隨機間隔的等待狀態,希望我能得到一些有關這方面的幫助。KVO綁定導致程序凍結?
當用戶啓動應用程序時,會顯示一個窗口,用於收集用戶的各種數據和選擇列表文件條目。然後用戶按下'run'按鈕,與'run'按鈕相關的app委託方法實例化一個類(TestBindingClass),該類中有一個方法,最終將在後臺運行。然後啓動第二個xib文件,顯示第二個包含空滾動視圖的窗口。作爲在windowDIdLoad方法中啓動第二個窗口的一部分,第二個窗口代碼註冊爲TestBindingClass屬性的觀察者。最後,主窗口'run'方法執行TestBindingClass中的第二個線程方法,該方法將條目添加到NSMutableArray中,並且每次添加新條目時也會觸發受監視的屬性。 NSMutableArray中的每個條目都是一行文本(字符串),它將顯示在第二個xib滾動視圖中。
TestBindingClass是一個類的(相對)空的shell,它將在其中進行大量的計算,當達到某些檢查點時它將發出狀態消息。這些檢查點消息將顯示在滾動視圖中。
大多數情況下,這工作正常。事實上,有時這可以完美地工作。其他時候,這個過程似乎凍結了,並且在掛起等待循環之前僅顯示一些狀態行。通常這是滾動視圖將要擴展超過窗口大小並且滾動條將被激活的點。奇怪的是,如果我將調試斷點添加到第二個xib代碼中的添加觀察器方法中,它總是正常工作。
這麼多的介紹...讓我們展示一些代碼....
下面是在主窗口中
-(IBAction)runButtonPressed:(id)sender
{
// do a bunch of stuff
TestBindingClass* tempTestBindingClass = [[TestBindingClass alloc] init];
RunResultWindowController = [[RunResultWindow alloc] initWithWindowNibName:@"RunResultWindow"];
RunResultWindowController.localTestBindingClass=tempTestBindingClass;
[RunResultWindowController showWindow:self];
[self.StatusDisplayOutput setStringValue:@"Run Complete"];
[tempTestBindingClass submitStatusStringSequence];
}
這裏的「運行」按鈕,方法的代碼是代碼對於上面提到的TestBindingClass。 StatusStrings是Mutable數組,它將包含應該最終顯示在滾動視圖中的狀態字符串列表,該視圖通過arraystatuscounter屬性觀察此類。
// TestBindingClass.h
#import <Foundation/Foundation.h>
@interface TestBindingClass : NSObject {
NSMutableArray *StatusStrings;
int arraystatuscounter;
}
@property (nonatomic, retain) NSMutableArray *StatusStrings;
@property int arraystatuscounter;
- (void) runStatusStringSequence:(id)param;
- (void) submitStatusStringSequence;
@end
這表明TestBindingClass的代碼,這是相當簡單的。只要將愚蠢的小字符串一次放入NSMutableArray中,並在每次向數組中添加一個字符串時調用arraystatuscounter屬性。
// TestBindingClass.m
- (void) submitStatusStringSequence
{
[NSThread detachNewThreadSelector:@selector(runStatusStringSequence:) toTarget:self withObject:nil];
}
- (void) runStatusStringSequence:(id)param
{
NSMutableArray *StatusStringsAlloc = [[NSMutableArray alloc] initWithCapacity:1];
StatusStrings = StatusStringsAlloc;
[StatusStrings addObject:[NSString stringWithString:@"first string"]];
[self setArraystatuscounter:1];
[StatusStrings addObject:[NSString stringWithString:@"second string"]];
[self setArraystatuscounter:2];
[StatusStrings addObject:[NSString stringWithString:@"third string"]];
[self setArraystatuscounter:3];
[StatusStrings addObject:[NSString stringWithString:@"fourth string"]];
[self setArraystatuscounter:4];
[StatusStrings addObject:[NSString stringWithString:@"fifth string"]];
[self setArraystatuscounter:5];
[StatusStrings addObject:[NSString stringWithString:@"sixth string"]];
[self setArraystatuscounter:6];
[StatusStrings addObject:[NSString stringWithString:@"seventh string"]];
[self setArraystatuscounter:7];
[StatusStrings addObject:[NSString stringWithString:@"last string"]];
[self setArraystatuscounter:8];
[StatusStrings addObject:[NSString stringWithString:@"first string"]];
[self setArraystatuscounter:1];
[StatusStrings addObject:[NSString stringWithString:@"second string"]];
[self setArraystatuscounter:2];
[StatusStrings addObject:[NSString stringWithString:@"third string"]];
[self setArraystatuscounter:3];
[StatusStrings addObject:[NSString stringWithString:@"fourth string"]];
[self setArraystatuscounter:4];
[StatusStrings addObject:[NSString stringWithString:@"fifth string"]];
[self setArraystatuscounter:5];
[StatusStrings addObject:[NSString stringWithString:@"sixth string"]];
[self setArraystatuscounter:6];
[StatusStrings addObject:[NSString stringWithString:@"seventh string"]];
[self setArraystatuscounter:7];
[StatusStrings addObject:[NSString stringWithString:@"last string"]];
[self setArraystatuscounter:8];
}
這是RunResultWindows窗口控制器代碼。 TestBindingClass的地址被添加到ivars中,以便它可以正確設置必要的KVO觀察者設置。
// RunResultWindow.h
@interface RunResultWindow : NSWindowController {
NSTextView *RunResultWindowTextView;
TestBindingClass *localTestBindingClass;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
@property (strong) IBOutlet NSTextView *RunResultWindowTextView;
@property (nonatomic, retain) TestBindingClass *localTestBindingClass;
@end
這裏是感興趣的RunResultWindow方法。請原諒arraycount if語句,這是爲了一些臨時的調試目的而添加的,並且永遠不會被刪除。
// RunResultWindow.m methods of interest
- (void)windowDidLoad
{
[super windowDidLoad];
NSWindow *wcWindow;
wcWindow = [self window];
[wcWindow makeKeyAndOrderFront:self];
NSString *teststring;
teststring = [NSString stringWithString: @"show first time window did load "];
[RunResultWindowTextView setString:teststring];
[RunResultWindowTextView display];
[localTestBindingClass addObserver:self
forKeyPath:@"arraystatuscounter"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
context:(void *)context
{
NSInteger arrayCount;
NSString* localDisplayString;
NSString* localNewlinePlusDisplayString;
NSTextStorage *tempTextStorage;
tempTextStorage = [RunResultWindowTextView textStorage];
arrayCount = [ localTestBindingClass.StatusStrings count ];
if (arrayCount >= 1) {
arrayCount--;
localDisplayString = [localTestBindingClass.StatusStrings objectAtIndex:arrayCount];
localNewlinePlusDisplayString = [@"\n" stringByAppendingString:localDisplayString];
[tempTextStorage beginEditing];
[tempTextStorage replaceCharactersInRange:NSMakeRange([tempTextStorage length] - 1, 0)
withString:localNewlinePlusDisplayString];
[tempTextStorage endEditing];
[RunResultWindowTextView display];
}
}
有一點我使用inserttext方法,而不是開始編輯/ endEditing排序,並且通常在添加到滾動視圖中的第一行或第二行之後凍結。再次,當我把它放到調試中,並將其放入obsereValueForKeyPath方法的斷點時,它可以正常工作。當我用上面顯示的方法替換insertText時,它工作得好多了,但仍不完全正確。 – 2011-12-24 06:50:00
一個有趣的問題。我不確定答案是什麼,但我認爲它可能是一個分離一個新線程,用相同方法更新相同觀測值6次,然後用觀察方法直接從觀測值中獲取新值的組合對象,而不是來自'change'字典。這是猜測,但那些是你的代碼中沒有聞到的東西。請注意,按照慣例,KVO確實起作用,您應該使用小寫字母開始變量名稱。你觀察到的變量是好的,但有一些沒有。 – jrturton 2011-12-24 07:25:19