如何从后台线程更新可可应用程序中的UI控件

发布于 2024-09-03 13:38:31 字数 1983 浏览 2 评论 0原文

以下是 .m 代码:

#import "ThreadLabAppDelegate.h"
@interface ThreadLabAppDelegate()

- (void)processStart;
- (void)processCompleted;

@end


@implementation ThreadLabAppDelegate

@synthesize isProcessStarted;

- (void)awakeFromNib {
    //Set levelindicator's maximum value
    [levelIndicator setMaxValue:1000];
}

- (void)dealloc {
    //Never called while debugging ????
    [super dealloc];
}

- (IBAction)startProcess:(id)sender {
    //Set process flag to true
    self.isProcessStarted=YES;

    //Start Animation
    [spinIndicator startAnimation:nil];

    //perform selector in background thread
    [self performSelectorInBackground:@selector(processStart) withObject:nil];
}

- (IBAction)stopProcess:(id)sender {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}

- (void)processStart {
    int counter = 0;
    while (counter != 1000) {
        NSLog(@"Counter : %d",counter);

        //Sleep background thread to reduce CPU usage
        [NSThread sleepForTimeInterval:0.01];

        //set the level indicator value to showing progress
        [levelIndicator setIntValue:counter];

        //increment counter
        counter++;
    }

    //Notify main thread for process completed
    [self performSelectorOnMainThread:@selector(processCompleted) withObject:nil waitUntilDone:NO];

}

- (void)processCompleted {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}
@end 

​​我需要根据上面的代码清除以下内容。

  1. 如何从 UI 控件中断/取消 processStart while 循环?
  2. 我还需要在主 UI 中显示计数器值,我想这是通过 PerformSelectorOnMainThread 和传递参数来完成的。只是想知道,还有其他方法吗?
  3. 当我的应用程序启动时,它在活动监视器中显示 1 个线程,但是当我在后台线程中启动 processStart() 时,它会创建两个新线程,这使得总共 3 个线程,直到或除非循环完成。完成循环后,我可以看到2 个线程。 所以,我的理解是,当我调用performSelectorInBackground时创建了2个线程,但是第三个线程呢?它是从哪里创建的?
  4. 如果每次调用选择器时线程计数都会增加怎么办?如何控制或者我的实现不适合此类要求?

谢谢

following is .m code:

#import "ThreadLabAppDelegate.h"
@interface ThreadLabAppDelegate()

- (void)processStart;
- (void)processCompleted;

@end


@implementation ThreadLabAppDelegate

@synthesize isProcessStarted;

- (void)awakeFromNib {
    //Set levelindicator's maximum value
    [levelIndicator setMaxValue:1000];
}

- (void)dealloc {
    //Never called while debugging ????
    [super dealloc];
}

- (IBAction)startProcess:(id)sender {
    //Set process flag to true
    self.isProcessStarted=YES;

    //Start Animation
    [spinIndicator startAnimation:nil];

    //perform selector in background thread
    [self performSelectorInBackground:@selector(processStart) withObject:nil];
}

- (IBAction)stopProcess:(id)sender {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}

- (void)processStart {
    int counter = 0;
    while (counter != 1000) {
        NSLog(@"Counter : %d",counter);

        //Sleep background thread to reduce CPU usage
        [NSThread sleepForTimeInterval:0.01];

        //set the level indicator value to showing progress
        [levelIndicator setIntValue:counter];

        //increment counter
        counter++;
    }

    //Notify main thread for process completed
    [self performSelectorOnMainThread:@selector(processCompleted) withObject:nil waitUntilDone:NO];

}

- (void)processCompleted {
    //Stop Animation
    [spinIndicator stopAnimation:nil];

    //set process flag to false
    self.isProcessStarted=NO;
}
@end 

I need to clear following things as per the above code.

  1. How to interrupt/cancel processStart while loop from UI control?
  2. I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?
  3. When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads.
    So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?
  4. What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?

Thanks

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

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

发布评论

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

评论(1

好菇凉咱不稀罕他 2024-09-10 13:38:31

如何从后台线程更新可可应用程序中的 UI 控件

简单: 不要。

如何从 UI 控件中断/取消 processStart while 循环?

processStart 之外,设置一个标志变量。在 processStart 内部,检查该标志并退出循环(如果已设置)。

不要尝试从另一个线程“杀死”一个线程。这总是一个坏主意。 通过设置标志来告诉线程是时候停止了,并让线程检查该标志并在适当的时间停止。

我还需要在主 UI 中显示计数器值,我想这是通过 PerformSelectorOnMainThread 并传递参数来完成的。只是想知道,还有其他方法吗?

是的。

当我的应用程序启动时,它在活动监视器中显示 1 个线程,但是当我在后台线程中启动 processStart() 时,它会创建两个新线程,这使得总共 3 个线程,直到或除非循环完成。完成循环后我可以看到 2 个线程。所以,我的理解是,当我调用performSelectorInBackground时创建了2个线程,但是第三个线程呢?它是从哪里创建的?

使用 Instruments 或 Shark 分析您的应用程序并查看。它可能是进度指示器的心跳线程。

如果每次调用选择器时线程计数都会增加怎么办?如何控制这种情况,或者我的实现不适合此类要求?

每个performSelectorInBackground:withObject:消息都会启动一个线程。如果您的线程计数没有减少,那是因为您的线程方法没有退出。如果您的线程数太高,(可能)是因为您启动了太多线程。


有一种更好的方法可以做到这一点。

首先,Cocoa 的一般规则是永不睡觉。可以将其视为特殊的高咖啡因可可。对于任何你可能在另一个框架中睡觉的事情,Cocoa 中几乎总是有更好、通常更简单的方法。

考虑到这一点,请查看processStart。它所做的只是每厘米做一些事情。如何最好地做到这一点?

Cocoa 有一个用于此特定目的的类:NSTimer。创建一个计时器,以所需的时间间隔向自己发送一条消息,并通过更新进度条来响应该消息 - 也就是说,您的计时器回调方法本质上应该只是来自 processStart 的循环体,而不需要环形。

顺便说一句,每秒 100 次更新有点过分了。首先,用户并不关心自上次更新进度条以来您已经取得了 1/5 像素的进度。其次,屏幕每秒仅更新约 60 次 ,所以更新任何可见的东西比这更快是没有意义的。

- (void)dealloc {
    //调试时从未调用过???
    [超级释放];
}

假设您将应用程序委托放在 MainMenu 笔尖中,应用程序对象因此拥有它,但它不知道这一点,因为它只知道应用程序委托作为其委托,这是一种非拥有关系。 (即使它是一种所有权关系,也只是两种所有权,应用程序将释放其中一种所有权,这无济于事。)

但是,应用程序委托的生命周期并不重要。它作为应用程序的委托的目的意味着它需要与应用程序一样持续,但是当应用程序消失时,进程正在退出,这意味着委托也将被释放,作为回收的一部分进程的内存空间。这就是为什么不调用 dealloc 的原因——整个进程空间立即消失,而不是一次释放一个对象。

所以,原则上,是的,应用程序委托没有被明确清理是有点狡猾的。实际上,不要在其 dealloc 中进行任何临时文件清理(使用 applicationWillTerminate: 代替),就可以了。

我通常通过将所有实际工作放入应用程序委托拥有的一个或多个其他对象中来解决该问题。应用程序委托在 applicationWillFinishLaunching: 中创建这些其他控制器,并在 applicationWillTerminate: 中释放它们,因此这些对象确实会获得 dealloc > 消息。问题解决了。

how to update UI controls in cocoa application from background thread

Simple: Don't.

How to interrupt/cancel processStart while loop from UI control?

Outside of processStart, set a flag variable. Inside of processStart, check that flag and exit the loop if it is set.

Don't try to “kill” a thread from another thread. It's always a bad idea. Tell the thread it's time to stop by setting the flag, and have the thread check that flag and stop at an appropriate time.

I also need to show the counter value in main UI, which i suppose to do with performSelectorOnMainThread and passing argument. Just want to know, is there anyother way to do that?

Yes.

When my app started it is showing 1 thread in Activity Monitor, but when i started the processStart() in background thread its creating two new thread,which makes the total 3 thread until or unless loop get finished.After completing the loop i can see 2 threads. So, my understanding is that, 2 thread created when i called performSelectorInBackground, but what about the thrid thread, from where it got created?

Profile your app using Instruments or Shark and look. It's probably the heartbeat thread for the progress indicator.

What if thread counts get increases on every call of selector.How to control that or my implementation is bad for such kind of requirements?

Every performSelectorInBackground:withObject: message starts a thread. If your thread count isn't going down, it's because your thread method didn't exit. If your thread count is too high, it's (probably) because you started too many threads.


There is a much better way to do this.

First, the general rule in Cocoa is never sleep. Think of this as special ultra-caffeinated Cocoa. For anything you might sleep for in another framework, there is almost always a better, usually easier, way in Cocoa.

With that in mind, look at processStart. All it does is do something every centisecond. How best to do that?

Cocoa has a class for this specific purpose: NSTimer. Create a timer that sends yourself a message at the desired interval, and respond to that message by updating the progress bar—that is, your timer callback method should essentially just be the loop body from processStart, without the loop.

By the way, 100 updates per second is overkill. First off, the user does not care that you have made 1/5th of a pixel's worth of progress since the last time you updated the bar. Second, the screen only updates about 60 times per second anyway, so updating anything visible faster than that is pointless.

- (void)dealloc {
    //Never called while debugging ????
    [super dealloc];
}

Assuming you put your app delegate in the MainMenu nib, the application object owns it because of that—but it doesn't know that, because it only knows about the app delegate as its delegate, which is a non-owning relationship. (And even if it were an owning relationship, that would just be two ownerships, of which the app would release one, which wouldn't help.)

However, the lifetime of the app delegate doesn't really matter. Its purpose as the delegate of the application means that it needs to last about as long as the application does, but when the application goes away, the process is exiting, which means the delegate will be deallocated as well, as part of the reclamation of the process's memory space. That's why dealloc isn't called—the whole process space goes away at once, instead of objects being deallocated one at a time.

So, in principle, yeah, the app delegate not getting explicitly cleaned up is kind of dodgy. In practice, don't put any temporary-files clean-up in its dealloc (use applicationWillTerminate: instead) and you'll be fine.

I typically work around the problem by putting all my real work in one or more other objects which the app delegate owns. The app delegate creates these other controllers in applicationWillFinishLaunching: and releases them in applicationWillTerminate:, so those objects do get dealloc messages. Problem solved.

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