Objective-C 闭包已转换为 Swift,但不断重复运行,没有完成且没有错误

标签 objective-c swift closures

该程序的 Swift 版本编译并运行没有任何错误。然而,它似乎在包含“as!TSLTransponderDataReceivedBlock”的行处重复,结果如该行右侧所示。程序执行永远不会超过该行代码。如果这个特定的闭包被注释掉,那么除了缺少的功能之外,其余代码似乎可以按预期运行。

Objective-C Code that works as expected
----------------------------------------
- (void)viewDidLoad {
[super viewDidLoad];

self.csvString = [NSMutableString stringWithFormat:@"DATE,EPC,FTID,INDX,PC,CRC,RSSI\n"];
// Use the single
_commander = ((TSLAppDelegate *)[UIApplication sharedApplication].delegate).commander;

// This formatter will convert any timestamps received
_dateFormatter = [[NSDateFormatter alloc] init];
_dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
_dateFormatter.timeZone = [NSTimeZone localTimeZone];

// Note: the weakSelf is used to avoid warning of retain cycles when self is used in Blocks
__weak typeof(self) weakSelf = self;


//
// Performing an inventory could potentially take a long time if many transponders are in range so it is best to handle responses asynchronously
//
// The TSLInventoryCommand is also a TSLAsciiResponder for inventory responses and can have a transponderDataReceivedBlock
// that is informed of each transponder as it is received

// Create a TSLInventoryCommand
_inventoryResponder = [[TSLInventoryCommand alloc] init];

//
// Use the TransponderData-based per transponder Block callback
//
_inventoryResponder.transponderDataReceivedBlock = ^(TSLTransponderData *transponder, BOOL moreAvailable)
{
    // Append the transponder EPC identifier and RSSI to the results
    weakSelf.partialResultMessage = [weakSelf.partialResultMessage stringByAppendingFormat:@"Date: %@   EPC: %@   Indx: %@   RSSI: %@\n\n",
                                     (transponder.timestamp == nil ) ? @"n/a" : [weakSelf.dateFormatter stringFromDate: transponder.timestamp],
                                     (transponder.epc == nil ) ? @"n/a" : transponder.epc,
                                     (transponder.index == nil ) ? @"n/a" : [NSString stringWithFormat:@"%04X", transponder.index.unsignedIntValue ],
                                     (transponder.rssi == nil ) ? @"n/a" : [NSString stringWithFormat:@"%3d", transponder.rssi.intValue]
                                     ];

    // This line saves the RFID tag info to the string that will be written out to the CSV file.
    self.csvString = [NSMutableString stringWithFormat:@"%@ %@", self.csvString, weakSelf.fullResultMessage];

    weakSelf.transpondersSeen++;

    // If this is the last transponder add a few blank lines
    if( !moreAvailable ) {
        weakSelf.partialResultMessage = [weakSelf.partialResultMessage stringByAppendingFormat:@"\nTransponders seen: %4d\n\n", weakSelf.transpondersSeen];
        weakSelf.transpondersSeen = 0;
    }

    // This changes UI elements so perform it on the UI thread
    // Avoid sending too many screen updates as it can stall the display
    if( !moreAvailable || _transpondersSeen < 3 || _transpondersSeen % 10 == 0 ) {
        [weakSelf performSelectorOnMainThread: @selector(updateResults:) withObject:weakSelf.partialResultMessage waitUntilDone:NO];
        weakSelf.partialResultMessage = @"";
    }
};


Swift 3 code that keeps repeating and never gets past the end of the closure:
-----------------------------------------------------------------------------
override func viewDidLoad() {
    super.viewDidLoad()
    csvString = "DATE,EPC,FTID,INDX,PC,CRC,RSSI\n"
    // Use the single
    commander = (UIApplication.shared.delegate as? TSLAppDelegate)?.commander
    // This formatter will convert any timestamps received
    dateFormatter = DateFormatter()
    dateFormatter?.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
    dateFormatter?.timeZone = NSTimeZone.local
        // Note: the weakSelf is used to avoid warning of retain cycles when self is used in Blocks
    weak var weakSelf: TSLInventoryViewController? = self
    //
    // Performing an inventory could potentially take a long time if many transponders are in range so it is best to handle responses asynchronously
    //
    // The TSLInventoryCommand is also a TSLAsciiResponder for inventory responses and can have a transponderDataReceivedBlock
    // that is informed of each transponder as it is received
    // Create a TSLInventoryCommand
    inventoryResponder = TSLInventoryCommand()
    //
    // Use the TransponderData-based per transponder Block callback
    //
    // Append the transponder EPC identifier and RSSI to the results
    weakSelf?.inventoryResponder!.transponderDataReceivedBlock = {(transponder: TSLTransponderData, moreAvailable: Bool) -> Void in
        weakSelf!.partialResultMessage = weakSelf!.partialResultMessage.appendingFormat("Date: %@   EPC: %@   Indx: %@   RSSI: %@\n\n",
            (transponder.timestamp == nil) ? "n/a" : (weakSelf!.dateFormatter?.string(from: transponder.timestamp))!,
            (transponder.epc == nil) ? "n/a" : transponder.epc,
            (transponder.index == nil) ? "n/a" : String(format: "%04X", CUnsignedInt(transponder.index)),
            (transponder.rssi == nil) ? "n/a" : String(format: "%3d", CInt(transponder.rssi)))

        // This line saves the RFID tag info to the string that will be written out to the CSV file.
        weakSelf?.csvString = "\(weakSelf?.csvString ?? "n/a") \(weakSelf!.fullResultMessage)"
        weakSelf!.transpondersSeen += 1
        // If this is the last transponder add a few blank lines
        if !moreAvailable {
            weakSelf!.partialResultMessage = weakSelf!.partialResultMessage.appendingFormat("\nTransponders seen: %4d\n\n", weakSelf!.transpondersSeen)
            weakSelf!.transpondersSeen = 0
        }
        // This changes UI elements so perform it on the UI thread
        // Avoid sending too many screen updates as it can stall the display
        if !moreAvailable || self.transpondersSeen < 3 || self.transpondersSeen % 10 == 0 {
            weakSelf!.performSelector(onMainThread: #selector(self.updateResults), with: weakSelf!.partialResultMessage, waitUntilDone: false)
            weakSelf!.partialResultMessage = ""
        }
    } as! TSLTransponderDataReceivedBlock         //Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10003a878)
....
}

非常感谢您的建议 - 谢谢。

“软弱的 self ?” inventory.Responder!.transponderDataReceivedBlock 引用的前缀已被删除,这没有什么区别。此外,闭包中的所有代码都被删除,只留下实际的闭包结构,甚至空闭包也显示出相同的行为。由于 TSLTransponderDataReceivedBlock 是在原始 Objective-C API 源代码中定义和处理的,因此 Swift 如何设置 Obj-C 使用的闭包结构似乎很可能存在问题。

使用“跳转到定义”对 TSLTransponderDataReceivedBlock 的 Objective-C 定义是:

///
/// The block type to handle transponder information received
///
/// @param transponder - the transponder information provided as a TSLTransponderData instance
/// @param moreAvailable - YES if there are more transponders to be delivered
///
typedef void(^TSLTransponderDataReceivedBlock)(TSLTransponderData *transponder, BOOL moreAvailable);

最佳答案

此问题的解决方案如下:

1) 更改: } 作为! TSL应答器数据接收 block 到: } 评语:去掉“as!”说明符

2)更改: weakSelf?.inventoryResponder!.transponderDataReceivedBlock = {(transponder: TSLTransponderData, moreAvailable: Bool) -> 无效

致: inventoryResponder!.transponderDataReceivedBlock = {(_ transponder: TSLTransponderData?, _ moreAvailable: Bool) -> 无效

评论:删除weakSelf?引用

注意:最终的工作构建是使用 Xcode 9 Beta 4/Swift 4 完成的

关于Objective-C 闭包已转换为 Swift,但不断重复运行,没有完成且没有错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45727580/

相关文章:

ios - 快速在每个页面上添加相同的导航栏

python - 使用 lambda 表达式进行字典理解无法产生所需的结果

ios - 自行传递参数:@selector

objective-c - NSDateComponents 中的纪元是什么意思?

ios - 如何初始化 NSString 的 NSArray?

rust - 无法为返回引用的闭包推断适当的生存期

ios - swift 中作为参数的闭包和函数之间的区别

iphone - Cocos2d - 我可以将多个 Sprite 帧加载到帧缓存中吗

ios - 更改语言而不即时重新启动不适用于全局声明

swift - 如何通过代码而不是拖放将 Storyboard中的多个按钮连接到一个 Action ?