У меня есть предыдущий опыт последовательного программирования на С, но это было в середине 80-х. Я новичок в ООП, Swift и многопоточном кодировании в целом. Я пишу небольшую программу, чтобы лучше понять все 3. Я смог создать функциональную программу, которая запускает два потока, каждый из которых считает до 200, а затем сбрасывает до 1 и возобновляет счет в бесконечном цикле. Значение каждого счетчика выводится на консоль, и у меня есть кнопки «Пуск» и «Стоп» для каждого потока, которые позволяют мне управлять ими отдельно. Все работает нормально, хотя я признаю, что мой код далек от совершенства (я не полностью уважаю инкапсуляцию, у меня есть несколько глобальных переменных, которые должны быть локальными и т. Д.). Моя главная проблема - попытаться вывести значение каждого счетчика потока в метку вместо того, чтобы печатать их на консоль. Когда я пытаюсь изменить содержимое любой из моих меток, я получаю сообщение «Неожиданно найден ноль при неявном развертывании необязательного значения» *
Когда я использую аналогичную строку кода внутри функции кнопки, она отлично работает.
Это содержимое моего файла ViewController.swift:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Call Async Task
startProgram()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
@IBOutlet var threadAValueLabel: NSTextField!
@IBOutlet var threadBValueLabel: NSTextField!
@IBAction func threadAStartButton(_ sender: NSButtonCell) {
threadAGoNoGo = 1
self.threadAValueLabel.stringValue = "Start"
}
@IBAction func threadAStopButton(_ sender: NSButton) {
threadAGoNoGo = 0
self.threadAValueLabel.stringValue = "Stop"
}
@IBAction func threadBStartButton(_ sender: NSButton) {
threadBGoNoGo = 1
self.threadBValueLabel.stringValue = "Start"
}
@IBAction func threadBStopButton(_ sender: NSButton) {
threadBGoNoGo = 0
self.threadBValueLabel.stringValue = "Stop"
}
func changethreadALabel(_ message: String) {
self.threadAValueLabel.stringValue = message
}
func changethreadBLabel(_ message: String) {
self.threadBValueLabel.stringValue = message
}
Код, создающий ошибку, находится в последних 2 методах:
self.threadAValueLabel.stringValue = message
и
self.threadBValueLabel.stringValue = message
Хотя следующий код внутри функции кнопки работает отлично.
self.threadBValueLabel.stringValue = "Stop"
Код, который создает два потока, следующий:
import Foundation
func startProgram(){
let myViewController: ViewController = ViewController (nibName:nil, bundle:nil)
// Start counting through 200 when Thread A start button is pressed and stop when Thread A Stop button is pressed. When reaching 200, go back to 0 and loop forever
DispatchQueue(label: "Start Thread A").async {
while true { // Loop Forever
var stepA:Int = 1
while stepA < 200{
for _ in 1...10000000{} // Delay loop
if threadAGoNoGo == 1{
print("Thread A \(stepA)")
myViewController.changethreadALabel("South \(stepA)") // Update Thread A value display label
stepA += 1
}
}
stepA = 1
}
}
// Start counting through 200 when Thread B start button is pressed and stop when Thread B Stop button is pressed. When reaching 200, go back to 0 and loop forever
DispatchQueue(label: "Start Thread B").async {
while true { // Loop Forever
var stepB:Int = 1
while stepB < 200{
for _ in 1...10000000{} // Delay loop
if threadBGoNoGo == 1{
print("Tread B \(stepB)")
myViewController.changethreadBLabel("South \(stepB)") // Update Thread B value display label
stepB += 1
}
}
stepB = 1
}
}
}
Вероятно, это очень просто для большинства из вас, но я провел четыре вечера, пытаясь выяснить сам и поискать на этом форуме, но безуспешно.
Новое редактирование:
Благодаря ответу Роба я смог прогрессировать, но я попал в другую ловушку. если я переместлю свой код в класс ViewController, я, кажется, смогу получить доступ к моим переменным stringValue self.threadAValueLabel.stringValue и self.threadBValueLabel.stringValue, но приведенные ниже строки генерируют «NSControl.stringValue должен использоваться только из основного потока» сообщение об ошибке:
self.threadAValueLabel.stringValue = message
self.threadBValueLabel.stringValue = message
Хотя я понимаю, что означает сообщение об ошибке, я пытался найти обходной путь к этой проблеме в течение нескольких часов, и, похоже, ничего не работает.
Вот полный код
import Foundation
import Cocoa
public var threadAGoNoGo:Int = 0
public var threadBGoNoGo:Int = 0
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Call Async Task
// Start counting through 200 when Thread A start button is pressed and stop when Thread A Stop button is pressed. When reaching 200, go back to 0 and loop forever
DispatchQueue(label: "Start Thread A").async {
while true { // Loop Forever
var stepA:Int = 1
while stepA < 200{
for _ in 1...10000000{} // Delay loop
if threadAGoNoGo == 1{
print("Thread A \(stepA)")
self.changethreadALabel("Thread A \(stepA)")
stepA += 1
}
}
stepA = 1
}
}
// Start counting through 200 when Thread B start button is pressed and stop when Thread B Stop button is pressed. When reaching 200, go back to 0 and loop forever
DispatchQueue(label: "Start Thread B").async {
while true { // Loop Forever
var stepB:Int = 1
while stepB < 200{
for _ in 1...10000000{} // Delay loop
if threadBGoNoGo == 1{
print("Tread B \(stepB)")
self.changethreadBLabel("Thread B \(stepB)")
stepB += 1
}
}
stepB = 1
}
}
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
@IBOutlet var threadAValueLabel: NSTextField!
@IBOutlet var threadBValueLabel: NSTextField!
@IBAction func threadAStartButton(_ sender: NSButtonCell) {
threadAGoNoGo = 1
self.threadAValueLabel.stringValue = "Start"
}
@IBAction func threadAStopButton(_ sender: NSButton) {
threadAGoNoGo = 0
self.threadAValueLabel.stringValue = "Stop"
}
@IBAction func threadBStartButton(_ sender: NSButton) {
threadBGoNoGo = 1
self.threadBValueLabel.stringValue = "Start"
}
@IBAction func threadBStopButton(_ sender: NSButton) {
threadBGoNoGo = 0
self.threadBValueLabel.stringValue = "Stop"
}
func changethreadALabel(_ message:String) {
self.threadAValueLabel.stringValue = message
}
func changethreadBLabel(_ message: String) {
self.threadBValueLabel.stringValue = message
}
}