Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
迭代数组时在iOS中发出更新标签_Ios_Objective C_Arrays_Loops - Fatal编程技术网

迭代数组时在iOS中发出更新标签

迭代数组时在iOS中发出更新标签,ios,objective-c,arrays,loops,Ios,Objective C,Arrays,Loops,我对iOS编程还不熟悉,但我已经找不到答案了。 在Xcode 5中,我在一个数组上迭代,并尝试在值发生变化时用值更新标签 这是.h文件 #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (strong, nonatomic) NSArray *currentNumber; @property (strong, nonatomic) IBOutlet UILabel *showL

我对iOS编程还不熟悉,但我已经找不到答案了。 在Xcode 5中,我在一个数组上迭代,并尝试在值发生变化时用值更新标签

这是.h文件

#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (strong, nonatomic) NSArray *currentNumber;
@property (strong, nonatomic) IBOutlet UILabel *showLabel;
- (IBAction)start;
@end 
这就是它变得棘手的地方

下面的工作非常完美

- (IBAction)start {
    self.showLabel.text = [NSString stringWithFormat:@"new text"];
}
@end
就像这样

- (IBAction)start {
    for (NSString *p in self.currentNumber) {
        NSLog(@"%@", p);
        sleep(3);
    }
}
@end
但是当我用设置.text属性替换NSLog时,它“失败”。计时仍然会发生,并且标签会在

- (IBAction)start {
    for (NSString *p in self.currentNumber) {
        self.showLabel.text = [NSString stringWithFormat:@"%@", p];
        sleep(3);
    }
}
@end
最后一点奇怪的是,如果我使用NSLog,并在调用“for”循环之前尝试更改.text属性,则在循环完成之前,文本更改将被忽略

- (IBAction)start {
    self.showLabel.text = [NSString stringWithFormat:@"5"];
    for (NSString *p in self.currentNumber) {
        NSLog(@"%@", p);
        sleep(3);
    }
}
@end
我错过了什么


(如果要查看源文件,可以在

处获取它们。如果要定期更新标签,请不要使用
睡眠
。如果在主线程上调用它,则会阻塞UI,这不是很理想

改用
N定时器
,使其每N秒点火一次

这样做可以:

- (void)startUpdatingLabel {
    [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(updateLabelWithIndex:) userInfo:@0 repeats:NO];
}

- (void)updateLabel:(NSTimer *)timer {
    NSInteger index = [timer.userInfo integerValue];
    if (index >= self.currentNumber.count) {
        return;
    }
    self.showLabel.text = [NSString stringWithFormat:@"%@", self.currentNumber[index]];
    [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(updateLabelWithIndex:) userInfo:@(index+1) repeats:NO];
}

每次调用
updateLabel:
时,它都会安排一个新的计时器,该计时器将在3秒内再次调用。每次增加并传递
索引值。

正如您所意识到的,UI只会在主线程处理事件时更新。在循环中,它不会更新

有两种方法可以解决这个问题

最简单的方法是在后台线程中执行循环。不过有一个缺点:这将允许用户继续与UI交互。而且,UI只能从主线程更新

您需要将工作分派到后台,然后让后台将工作分派回主线程

这听起来很复杂,事实确实如此。谢天谢地,苹果公司在Objective-C中添加了块和Grand Central Dispatch。你可以用它们来分解代码块,确保它们在正确的线程上执行

- (IBAction)start {
    [self disableMyUI];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_NORMAL, 0), ^{
        // this code will be executed "later", probably after start has returned.
        // (in all cases, later should be considered "soon but not immediately.")
        for (NSString *p in self.currentNumber) {
            dispatch_async(dispatch_get_main_queue(),^{
                // this code will be executed "later" by the main loop.
                // You may have already moved on to the next thing, and even
                // dispatched the next UI update.
                // Don't worry; the main queue does things in order.
                self.showLabel.text = [NSString stringWithFormat:@"%@", p];
            });
            sleep(3); // do your heavy lifting here, but keep in mind:
                      // you're on a background thread.
        }
        dispatch_async(dispatch_get_main_queue,^{
            // this occurs "later," but after other all other UI events queued
            // to the main queue.
            [self enableMyUI];
        });
    }
    // this line of code will run before work is complete
}
您必须编写
禁用yui
启用yui
;确保它们禁用所有内容(包括使用导航时的后退按钮、使用选项卡栏控制器时的选项卡栏等)

另一种解决方法是使用
NSTimer
。但是,如果你这样做,你仍然在主线程上完成工作。如果你可以将工作分割成可预测的小部分,这是可行的,但你最好在后台线程上完成

需要记住的一点是:虽然在开发过程中不太可能遇到问题,但在主线程上执行繁重的工作会导致用户崩溃。在iOS上,有一个进程可以监视应用程序是否响应事件,如绘图更新。如果应用程序没有及时响应事件,则将导致终止所以,你不需要忍受UI更新的缺失;你只需要从后台线程执行耗时的操作

另见:


永远不要在主线程上调用
sleep
sleep()
大概是主线程上繁重工作的占位符。这也不应该在主线程上完成。:)Steven,感谢您的回复。由于必须编写
禁用yui
启用yui
&块,这超出了我目前的技能。另外,我可能没有在原始问题->中传达这一部分,一旦用户开始这个过程,就不需要进一步的交互。(除了暂停按钮,我将在后面处理)界面将只显示预定的数据。这就是为什么我包括它。无论您使用计时器还是块方法,您仍然必须禁用UI,否则用户将能够与其交互。即使你不想让他们这么做!谢谢加布里埃尔,这很好用!这正是我需要的。(我只需要重新命名一些东西…)
- (IBAction)start {
    [self disableMyUI];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_NORMAL, 0), ^{
        // this code will be executed "later", probably after start has returned.
        // (in all cases, later should be considered "soon but not immediately.")
        for (NSString *p in self.currentNumber) {
            dispatch_async(dispatch_get_main_queue(),^{
                // this code will be executed "later" by the main loop.
                // You may have already moved on to the next thing, and even
                // dispatched the next UI update.
                // Don't worry; the main queue does things in order.
                self.showLabel.text = [NSString stringWithFormat:@"%@", p];
            });
            sleep(3); // do your heavy lifting here, but keep in mind:
                      // you're on a background thread.
        }
        dispatch_async(dispatch_get_main_queue,^{
            // this occurs "later," but after other all other UI events queued
            // to the main queue.
            [self enableMyUI];
        });
    }
    // this line of code will run before work is complete
}