KVO 绑定导致程序冻结?

发布于 2024-12-22 20:45:00 字数 6010 浏览 2 评论 0原文

我有一个奇怪的问题,涉及一个进程,该进程似乎以随机的间隔陷入等待状态,希望我能得到一些有关此问题的帮助。

当用户启动应用程序时,会显示一个窗口,该窗口收集用户的各种数据和选择列表文件条目。然后,用户按下“运行”按钮,与“运行”按钮相关的应用程序委托方法会实例化一个类 (TestBindingClass),该类中包含一个最终将在后台运行的方法。然后它会启动第二个 xib 文件,该文件显示第二个窗口,其中包含一个空的滚动视图。作为在 windowDIdLoad 方法中启动第二个窗口的一部分,第二个窗口代码注册为 TestBindingClass 属性的观察者。最后,主窗口“run”方法执行 TestBindingClass 内的第二个线程方法,该方法将条目添加到 NSMutableArray 中,并且每次添加新条目时都会触发受监视的属性。 NSMutableArray 中的每个条目都是一行文本(字符串),它将显示在第二个 xib 滚动视图中。

TestBindingClass 是一个类的(相对)空壳,其中将进行大量计算,当到达某些检查点时将发出状态消息。这些检查点消息将显示在滚动视图中。

大多数情况下,这工作得很好。事实上,有时这很有效。其他时候,该进程似乎冻结了,并且在挂起等待循环之前仅显示一些状态行。通常这是滚动视图即将扩展超过窗口大小并且滚动条将被激活的点。奇怪的是,如果我将调试断点添加到第二个 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 是可变数组,它将包含状态字符串列表,该列表最终应显示在通过 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];
  }

 }

I have sort of an odd question concerning a process which appears to get stuck in a wait status at what appears to be random intervals and am hoping I can get some help concerning this.

When the user starts the application, a window is displayed which collects various data and pick list file entries from the user. The user then presses a 'run' button and the app delegate method related to the 'run' button instantiates a class (TestBindingClass) which has a method in it which will eventually be run in the background. It then fires up a second xib file which displays a second window which contains an empty scroll view. As part of initiating the second window in the windowDIdLoad method, the second window code registers as an observer of a property of TestBindingClass. Finally, the main window 'run' method has executes the second thread method inside of TestBindingClass which adds entries into a NSMutableArray and also triggers the monitored property each time a new entry is added. Each entry into the NSMutableArray is a line of text (a string) which is to be displayed into the second xib scroll view.

TestBindingClass is the (relatively) empty shell of a class which will have extensive calculations in it which will emit status messages as certain checkpoints are reached. Those check point messages will be displayed in the scroll view.

Mostly, this works fine. In fact, sometimes this works perfectly. Other times the process seems to freeze up and only displays a few of the status lines before hanging up in a wait loop. Usually this is a the point where the scroll view is about to expand past the window size and the scroll bar would become activated. The odd thing is that if I add debugging breakpoints into the add observer method in the second xib code, it ALWAYS works properly.

So much for the description... let's show some code....

Here is the code for the 'run' button method in the main window

-(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];

}

Here is the code for the TestBindingClass as referenced above. StatusStrings is the Mutable array which will contain the list of status strings which should be eventually displayed in the scroll view which is observing this class via the arraystatuscounter property.

 //  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

This shows the code of TestBindingClass which is pretty simple. Just drop goofy little strings into the NSMutableArray one at a time and dinks the arraystatuscounter property each time it adds a string into the array.

 //  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];

 }

Here is the RunResultWindows window controller code. The address for the TestBindingClass is added into the ivars so that it can properly set up the necessary KVO observer settings.

 //  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

Here are the RunResultWindow methods of interest. Please excuse the arraycount if statement wwhich was added in there for some temporary debugging purposes and never removed.

 //  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];
  }

 }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

幼儿园老大 2024-12-29 20:45:00

我认为 KVO 与此没有太大关系。你所描述的“非常简单”实际上非常非常复杂。这就是罪魁祸首:-

[NSThread detachNewThreadSelector:@selector(runStatusStringSequence:) toTarget:self withObject:nil]

一旦你这样做了,你需要的不仅仅是对 Cocoa 框架的良好理解 - 你需要对多线程编程有很好的理解,并且对 Cocoa 框架如何在多线程世界中工作有很好的理解。这很复杂也很困难。

您至少需要研究这些

http: //developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html

http://developer.apple.com/library/mac/documentation /Cocoa/Conceptual/Multithreading/Multithreading.pdf

如果您完全删除第二个线程,我仍然不希望它一直工作(尽管它可能在某些时候工作)。您不应该直接在文本视图上调用 -display 。视图的绘制、设置绘制上下文、刷新率等均由框架为您处理。

只要在不知道当前绘图上下文状态的情况下随机调用 -display 就足以使您的应用程序崩溃(您可以轻松地绘制到任何随机的内存中)。将线程添加到混合物中,您就会遇到更多麻烦。

您的方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

在后台线程上调用,但 gui 是不是线程安全的,必须在主线程上运行,包括与文本视图等 gui 对象的任何交互。

I don't think the KVO has much to do with it. The bit you describe as 'pretty simple' is actually really, really complicated. This is the culprit:-

[NSThread detachNewThreadSelector:@selector(runStatusStringSequence:) toTarget:self withObject:nil]

As soon as you do this you need more than a good understanding of the Cocoa frameworks - you need a good understanding of multithreaded programming and a good understanding of how the Cocoa frameworks work in a multithreaded world. It's complicated and it is hard.

You would at least need to study these

http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html

http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/Multithreading.pdf

If you removed the second thread altogether i still wouldn't expect this to work all the time (although it might work some of the time). You should not be calling -display on the text view directly. Drawing of the views, setting up drawing contexts, refresh rate, etc. is handled for you by the framework.

Just calling -display at a random time without knowing the status of the current drawing context would be enough to crash your app (you could easily be drawing into any random piece of memory). Add the threading into the mix and you are in more trouble.

Your method

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

is called on the background thread but the gui is not thread safe and must run on the main thread, including any interactions with gui objects like text views.

玻璃人 2024-12-29 20:45:00

这是实际有效的代码。请原谅错误的大小写以及解决方案的先前迭代中偶尔留下的一些奇怪代码。

这是主窗口中“运行”按钮方法的代码...

- (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.outputfilestring1 becomeFirstResponder];

    [tempTestBindingClass submitStatusStringSequence];

}

这是上面引用的 TestBindingClass 的代码。 StatusStrings 是可变数组,它将包含状态字符串列表,该列表最终应显示在通过 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 属性以启动 KVO 观察方法。第一个主要区别是我使用了上面建议的 GCD 方法...

//  TestBindingClass.m

 - (void) submitStatusStringSequence
 {
     NSString *parameterString;
    [self runStatusStringSequence: parameterString];
 }


 - (void) runStatusStringSequence:(id)param 
 {

dispatch_queue_t backgroundQueue = dispatch_queue_create("Background Queue",NULL);

dispatch_async(backgroundQueue, ^{ 

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];

dispatch_release(backgroundQueue);

});


 }

这是 RunResultWindows 窗口控制器代码。 TestBindingClass 的地址被添加到 ivars 中,以便它可以正确设置必要的 KVO 观察器设置。另外,我强制该方法在主线程上运行,因为我想它是在后台线程上运行的,因为它是由已经在后台线程上运行的进程调用的。

@interface RunResultWindow : NSWindowController {

    NSTextView *RunResultWindowTextView;
    TestBindingClass *localTestBindingClass;

}

- (IBAction)FinishButtonPush:(id)sender;
- (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 语句,该语句是为了一些临时调试目的而添加的,并且从未被删除。请注意,它被强制到主线程。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change 
        context:(void *)context
{    

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{ 

    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 setNeedsDisplay:YES];}

    });
}

非常感谢 hooleyhoop 为我们指明了正确的方向。

Here is the code that actually worked. Please excuse the mis-capitilizations and the occasional bit of odd code left over from a previous iteration of the solution.

Here is the code for the 'run' button method in the main window...

- (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.outputfilestring1 becomeFirstResponder];

    [tempTestBindingClass submitStatusStringSequence];

}

Here is the code for the TestBindingClass as referenced above. StatusStrings is the mutable array which will contain the list of status strings which should be eventually displayed in the scroll view which is observing this class via the arraystatuscounter property.

 //  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

This shows the implementation code of TestBindingClass. It just drops goofy little strings into the NSMutableArray one at a time and dinks the arraystatuscounter property each time it adds a string into the array to set off the KVO observation method. The first major difference is that I used the GCD methods as suggested above...

//  TestBindingClass.m

 - (void) submitStatusStringSequence
 {
     NSString *parameterString;
    [self runStatusStringSequence: parameterString];
 }


 - (void) runStatusStringSequence:(id)param 
 {

dispatch_queue_t backgroundQueue = dispatch_queue_create("Background Queue",NULL);

dispatch_async(backgroundQueue, ^{ 

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];

dispatch_release(backgroundQueue);

});


 }

Here is the RunResultWindows window controller code. The address for the TestBindingClass is added into the ivars so that it can properly set up the necessary KVO observer settings. Also, I forced the method to run on the main thread because, I suppose, it was running on the background thread because it was invoked by a process already running on the background thread.

@interface RunResultWindow : NSWindowController {

    NSTextView *RunResultWindowTextView;
    TestBindingClass *localTestBindingClass;

}

- (IBAction)FinishButtonPush:(id)sender;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

@property (strong) IBOutlet NSTextView *RunResultWindowTextView;
@property (nonatomic, retain) TestBindingClass *localTestBindingClass;

@end

Here are the RunResultWindow methods of interest. Please excuse the arraycount if statement which was added in there for some temporary debugging purposes and never removed. Note that it was forced to the main thread.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change 
        context:(void *)context
{    

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{ 

    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 setNeedsDisplay:YES];}

    });
}

Much thanks to hooleyhoop for the good pointers to the right direction.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文