Быстрое выявление сильных эталонных циклов
Завершите Swift Noob здесь. У меня проблема с моим кодом, которая, как мне кажется, связана с сильными ссылочными циклами. Я потратил несколько часов на изучение сильных эталонных циклов и до сих пор не понимаю, что они из себя представляют, и где они находятся в моем коде.
Для кого-то с половиной мозгов я уверен, что это очень простое решение, но, к сожалению, для меня, я не могу понять это.
Любая помощь будет принята с благодарностью!
Ниже приведены два из моих контроллеров представления. Первый контроллер представления (DevicesPage) создает табличное представление со списком устройств Bluetooth. Этот контроллер просмотра также подключается к устройству.
// Devices Page
import Foundation
import UIKit
import CoreBluetooth
// Declare the variables
weak var txCharacteristic : CBCharacteristic?
weak var rxCharacteristic : CBCharacteristic?
weak var batteryCharacteristic : CBCharacteristic?
weak var blePeripheral : CBPeripheral?
var characteristicASCIIValue = NSString()
var batteryLevel = String()
class DevicesPage: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDelegate, UITableViewDataSource{
// Declare some more variables
var centralManager : CBCentralManager!
var data = NSMutableData()
var writeData: String = ""
var peripherals: [CBPeripheral] = []
var characteristicValue = [CBUUID: NSData]()
var characteristics = [String : CBCharacteristic]()
var connectionStatus = "Disconnected"
var timer: Timer?
var activity: UIActivityIndicatorView!
var refreshTimer: Timer?
var oldCount = 0
//UI
@IBOutlet var baseTableView: UITableView!
@objc func refreshAction() {
if connectionStatus == "Disconnected"
{
disconnectFromDevice()
self.peripherals = []
self.baseTableView.reloadData()
startScan()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.baseTableView.delegate = self
self.baseTableView.dataSource = self
self.baseTableView.reloadData()
activity = UIActivityIndicatorView(style: .gray)
activity.center = CGPoint(x: 198, y: 65)
baseTableView.addSubview(activity)
/*Our key player in this app will be our CBCentralManager. CBCentralManager objects are used to manage discovered or connected remote peripheral devices (represented by CBPeripheral objects), including scanning for, discovering, and connecting to advertising peripherals.
*/
}
override func viewDidAppear(_ animated: Bool) {
disconnectFromDevice()
super.viewDidAppear(animated)
refreshScanView()
print("View Cleared")
activity.startAnimating()
centralManager = CBCentralManager(delegate: self, queue: nil)
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(sendPeripheralStateChange), userInfo: nil, repeats: true)
refreshTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(refreshAction), userInfo: nil, repeats: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("Stop Scanning")
centralManager?.stopScan()
//timer?.invalidate()
//refreshTimer?.invalidate()
}
/*Okay, now that we have our CBCentalManager up and running, it's time to start searching for devices. You can do this by calling the "scanForPeripherals" method.*/
func startScan() {
peripherals = []
print("Now Scanning...")
centralManager?.scanForPeripherals(withServices: [BLEService_UUID] , options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
}
/*We also need to stop scanning at some point so we'll also create a function that calls "stopScan"*/
@objc func cancelScan() {
self.centralManager?.stopScan()
print("Scan Stopped")
print("Number of Peripherals Found: \(peripherals.count)")
}
func refreshScanView() {
baseTableView.reloadData()
}
//-Terminate all Peripheral Connection
/*
Call this when things either go wrong, or you're done with the connection.
This cancels any subscriptions if there are any, or straight disconnects if not.
(didUpdateNotificationStateForCharacteristic will cancel the connection if a subscription is involved)
*/
func disconnectFromDevice () {
if blePeripheral != nil {
// We have a connection to the device but we are not subscribed to the Transfer Characteristic for some reason.
// Therefore, we will just disconnect from the peripheral
centralManager?.cancelPeripheralConnection(blePeripheral!)
}
}
func restoreCentralManager() {
//Restores Central Manager delegate if something went wrong
centralManager?.delegate = self
}
/*
Called when the central manager discovers a peripheral while scanning. Also, once peripheral is connected, cancel scanning.
*/
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,advertisementData: [String : Any], rssi RSSI: NSNumber) {
blePeripheral = peripheral
self.peripherals.append(peripheral)
peripheral.delegate = self
self.baseTableView.reloadData()
if blePeripheral == nil {
print("Found new pheripheral devices with services")
print("Peripheral name: \(String(describing: peripheral.name))")
print("**********************************")
print ("Advertisement Data : \(advertisementData)")
}
}
//Peripheral Connections: Connecting, Connected, Disconnected
//-Connection
func connectToDevice () {
centralManager?.connect(blePeripheral!, options: nil)
}
/*
Invoked when a connection is successfully created with a peripheral.
This method is invoked when a call to connect(_:options:) is successful. You typically implement this method to set the peripheral’s delegate and to discover its services.
*/
//-Connected
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("*****************************")
print("Connection complete")
print("Peripheral info: \(String(describing: blePeripheral))")
//Stop Scan- We don't need to scan once we've connected to a peripheral. We got what we came for.
centralManager?.stopScan()
print("Scan Stopped")
//Erase data that we might have
data.length = 0
//Discovery callback
peripheral.delegate = self
//Only look for services that matches transmit uuid
peripheral.discoverServices(nil)
//print(peripheral.services!)
//Once connected, move to new view controller to manage incoming and outgoing data
//let storyboard = UIStoryboard(name: "Main", bundle: nil)
//let TempPage = storyboard.instantiateViewController(withIdentifier: "TempPage") as! TempPage
performSegue(withIdentifier: "Go", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destination = segue.destination as! TempPage
destination.peripheral = blePeripheral
}
/*
Invoked when the central manager fails to create a connection with a peripheral.
*/
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
if error != nil {
print("Failed to connect to peripheral")
return
}
}
func disconnectAllConnection() {
centralManager.cancelPeripheralConnection(blePeripheral!)
}
/*
Invoked when you discover the peripheral’s available services.
This method is invoked when your app calls the discoverServices(_:) method. If the services of the peripheral are successfully discovered, you can access them through the peripheral’s services property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
*/
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
print("*******************************************************")
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let services = peripheral.services else {
return
}
//We need to discover the all characteristic
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
// bleService = service
}
print("Discovered Services: \(services)")
}
/*
Invoked when you discover the characteristics of a specified service.
This method is invoked when your app calls the discoverCharacteristics(_:for:) method. If the characteristics of the specified service are successfully discovered, you can access them through the service's characteristics property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
*/
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
print("*******************************************************")
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let characteristics = service.characteristics else {
return
}
print("Found \(characteristics.count) characteristics!")
for characteristic in characteristics {
//looks for the right characteristic
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Rx) {
rxCharacteristic = characteristic
//Once found, subscribe to the this particular characteristic...
peripheral.setNotifyValue(true, for: rxCharacteristic!)
// We can return after calling CBPeripheral.setNotifyValue because CBPeripheralDelegate's
// didUpdateNotificationStateForCharacteristic method will be called automatically
peripheral.readValue(for: characteristic)
print("Rx Characteristic: \(characteristic.uuid)")
}
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Tx){
txCharacteristic = characteristic
print("Tx Characteristic: \(characteristic.uuid)")
}
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Battery) {
batteryCharacteristic = characteristic
peripheral.setNotifyValue(true, for: batteryCharacteristic!)
peripheral.readValue(for: characteristic)
print("Battery Characteristic: \(characteristic.uuid)")
}
peripheral.discoverDescriptors(for: characteristic)
}
}
// Getting Values From Characteristic
/*After you've found a characteristic of a service that you are interested in, you can read the characteristic's value by calling the peripheral "readValueForCharacteristic" method within the "didDiscoverCharacteristicsFor service" delegate.
*/
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic == rxCharacteristic {
if let ASCIIstring = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) {
characteristicASCIIValue = ASCIIstring
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: nil)
connectionStatus = "Connected!"
}
}
if characteristic == batteryCharacteristic {
batteryLevel = String(characteristic.value!.first!)
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: nil)
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
print("*******************************************************")
if error != nil {
print("\(error.debugDescription)")
return
}
if ((characteristic.descriptors) != nil) {
for x in characteristic.descriptors!{
let descript = x as CBDescriptor?
print("function name: DidDiscoverDescriptorForChar \(String(describing: descript?.description))")
print("Rx Value \(String(describing: rxCharacteristic?.value))")
print("Tx Value \(String(describing: txCharacteristic?.value))")
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
print("*******************************************************")
if (error != nil) {
print("Error changing notification state:\(String(describing: error?.localizedDescription))")
} else {
print("Characteristic's value subscribed")
}
if (characteristic.isNotifying) {
print ("Subscribed. Notification has begun for: \(characteristic.uuid)")
}
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
connectionStatus = "Disconnected"
print("Disconnected")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
print("Error discovering services: error")
return
}
//print("Message sent")
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
guard error == nil else {
print("Error discovering services: error")
return
}
print("Succeeded!")
}
//Table View Functions
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.peripherals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Connect to device where the peripheral is connected
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! PeripheralTableViewCell
let peripheral = self.peripherals[indexPath.row]
if peripheral.name == nil {
cell.peripheralLabel.text = "nil"
} else {
cell.peripheralLabel.text = peripheral.name
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
blePeripheral = peripherals[indexPath.row]
connectToDevice()
}
/*
Invoked when the central manager’s state is updated.
This is where we kick off the scan if Bluetooth is turned on.
*/
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == CBManagerState.poweredOn {
// We will just handle it the easy way here: if Bluetooth is on, proceed...start scan!
print("Bluetooth Enabled")
startScan()
} else {
//If Bluetooth is off, display a UI alert message saying "Bluetooth is not enable" and "Make sure that your bluetooth is turned on"
print("Bluetooth Disabled- Make sure your Bluetooth is turned on")
let alertVC = UIAlertController(title: "Bluetooth is not enabled", message: "Make sure that your bluetooth is turned on", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "ok", style: UIAlertAction.Style.default, handler: { (action: UIAlertAction) -> Void in
self.dismiss(animated: true, completion: nil)
})
alertVC.addAction(action)
self.present(alertVC, animated: true, completion: nil)
}
}
//@IBAction func unwindToDevicesPage(_ unwindSegue: UIStoryboardSegue) {
//}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Devices"
}
@objc func sendPeripheralStateChange()
{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "connectionStatus"), object: self)
}
}
Контроллер второго представления, данные которого передаются от первого контроллера представления. Моя проблема лежит где-то здесь, я верю...
// Temp Page
import UIKit
import AVFoundation
import CoreBluetooth
import MessageUI
import Foundation
class TempPage: UIViewController, CBPeripheralManagerDelegate {
//Data
var peripheralManager: CBPeripheralManager?
var peripheral: CBPeripheral!
private var consoleAsciiText:NSAttributedString? = NSAttributedString(string: "")
var rawValue = ""
var timer: Timer?
var isConnected = true
var accel = Double()
var startButtonCounter = 1
var n = 0
var i = 0
@IBOutlet weak var connectionStateLabel: UILabel!
@IBOutlet weak var bluetoothButton: UIButton!
@IBOutlet weak var batteryLevelLabel: UILabel!
@IBOutlet weak var fetchTempButton: UIButton!
@IBOutlet weak var waveStartStopButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
//Create and start the peripheral manager
peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
//-Notification for updating the text view with incoming text
updateIncomingData()
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(checkPeripheralStateChange), userInfo: nil, repeats: true)
//stopData()
rawValue = ""
}
override func viewDidDisappear(_ animated: Bool) {
peripheralManager?.stopAdvertising()
self.peripheralManager = nil
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
stopData()
rawValue = ""
}
func updateIncomingData () {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "Notify"), object: nil , queue: nil){ [unowned self]
notification in
self.rawValue = characteristicASCIIValue as String
self.n = self.n + 1
print(self.n)
print(characteristicASCIIValue)
self.batteryLevelLabel.text = ("\(String(batteryLevel))%")
}
}
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
if peripheral.state == .poweredOn {
}
print("Peripheral manager is running")
}
//Check when someone subscribes to our characteristic, start sending the data
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print("Device subscribed to characteristic")
}
func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager, error: Error?) {
if let error = error {
print("\(error)")
return
}
}
//Update the connected state label to show when the device is connected
@objc func checkPeripheralStateChange()
{
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "connectionStatus"), object: nil, queue: nil) { [unowned self] (Notification) in
let devicesPage = Notification.object as! DevicesPage
self.connectionStateLabel.text = devicesPage.connectionStatus
if self.connectionStateLabel.text == "Disconnected"
{
self.connectionStateLabel.textColor = .red
}
else
{
if self.isConnected == true {
self.isConnected = false
}
self.connectionStateLabel.textColor = UIColor(red: 0.5647, green: 0.7373, blue: 0.8157, alpha: 1.0)
}
}
}
// Write functions
func writeValue(data: String){
let valueString = (data as NSString).data(using: String.Encoding.utf8.rawValue)
//change the "data" to valueString
if let blePeripheral = blePeripheral{
if let txCharacteristic = txCharacteristic {
blePeripheral.writeValue(valueString!, for: txCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func writeCharacteristic(val: Int8){
var val = val
let ns = NSData(bytes: &val, length: MemoryLayout<Int8>.size)
blePeripheral!.writeValue(ns as Data, for: txCharacteristic!, type: CBCharacteristicWriteType.withResponse)
}
@IBAction func pressedFetchTempButton(_ sender: Any) {
startF()
}
/*
@objc func readTempF()
{
let temp = Double(self.rawValue)
}
@objc func readAcc()
{
print(self.rawValue)
}
*/
@objc func startF() {
writeValue(data: "F")
}
@objc func startAcc() {
writeValue(data: "S")
}
func stopData() {
writeValue(data: "T")
}
func fetchBattery() {
writeValue(data: "B")
}
@IBAction func pressedStartStopButton(_ sender: Any) {
startButtonCounter = startButtonCounter + 1
if startButtonCounter % 2 == 0 {
// startWaveTimer()
waveStartStopButton.setTitle("Reset", for: .normal)
}
else {
// resetWaveTimer()
waveStartStopButton.setTitle("Start", for: .normal)
}
}
@IBAction func returnToDevicesPage(_ sender: Any) {
navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)
}
}