草庐IT

ios - 为什么我刚创建的指针抛出 KERN_INVALID_ADDRESS?

coder 2023-09-16 原文

所以与此类似recently posted question ,我在将 Amazon 的 AWS Obj-C 库与我的 Swift 应用程序集成时遇到问题。我有一个 NSOperation,它使用 Transfer Utility library 处理文件上传到 S3其中包括对后台文件传输的支持。最近发布了我们的应用程序后,我发现当应用程序返回前台时重新连接进度处理程序的代码发生了一些崩溃。代码改编自their Obj-C example :

- (void)viewDidLoad {
    [super viewDidLoad];

    ...

    AWSS3TransferUtility *transferUtility = [AWSS3TransferUtility defaultS3TransferUtility];
    [transferUtility
     enumerateToAssignBlocksForUploadTask:^(AWSS3TransferUtilityUploadTask *uploadTask, __autoreleasing AWSS3TransferUtilityUploadProgressBlock *uploadProgressBlockReference, __autoreleasing AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference) {
         NSLog(@"%lu", (unsigned long)uploadTask.taskIdentifier);

         // Use `uploadTask.taskIdentifier` to determine what blocks to assign.

         *uploadProgressBlockReference = // Reassign your progress feedback block.
         *completionHandlerReference = // Reassign your completion handler.
     }
     downloadTask:^(AWSS3TransferUtilityDownloadTask *downloadTask, __autoreleasing AWSS3TransferUtilityDownloadProgressBlock *downloadProgressBlockReference, __autoreleasing AWSS3TransferUtilityDownloadCompletionHandlerBlock *completionHandlerReference) {
         NSLog(@"%lu", (unsigned long)downloadTask.taskIdentifier);

         // Use `downloadTask.taskIdentifier` to determine what blocks to assign.

         *downloadProgressBlockReference =  // Reassign your progress feedback block.
         *completionHandlerReference = // Reassign your completion handler.
     }];
}

我的 Swift 版本在尝试取消引用 newProgressPointer 时因 EXC_BAD_ACCESS KERN_INVALID_ADDRESS 而崩溃:

// Swift 2.3

class AttachmentQueue: NSOperationQueue {

    ...

    /**
     Recreates `UploadOperation` instances for any that were backgrounded by the user leaving the
     app.
     */
    func addBackgroundedOperations() {
        let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
        transferUtility.enumerateToAssignBlocksForUploadTask({ (task, progress, completion) -> Void in
            guard let operation = UploadOperation(task: task, oldProgressPointer: progress, oldCompletionPointer: completion) else { return }
            self.addOperation(operation)
        }, downloadTask: nil)
    }

}
 /// An `UploadOperation` is an `NSOperation` that is responsible for uploading an attachment asset
 /// file (photo or video) to Amazon S3. It leans on `AWSS3TransferUtility` to get the actual
 /// uploading done.
 class UploadOperation: AttachmentOperation {

    ...

    /// An `AutoreleasingUnsafeMutablePointer` to the upload progress handler block.
    typealias UploadProgressPointer = AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityTask, NSProgress) -> Void)?>

    /// An `AutoreleasingUnsafeMutablePointer` to the upload completion handler block.
    typealias UploadCompletionPointer = AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityUploadTask, NSError?) -> Void)?>


     /**
      A convenience initializer to be used to re-constitute an `AWSS3TransferUtility` upload task that
      has been moved to the background. It should be called from `.enumerateToAssignBlocksForUploadTask()`
      when the app comes back to the foreground and is responsible for re-hooking-up its progress and
      completion handlers.

      - parameter task:                 The `AWSS3TransferUtilityTask` that needs re-hooking-up.
      - parameter oldProgressPointer:   An `AutoreleasingUnsafeMutablePointer` to the original progress handler.
      - parameter oldCompletionPointer: An `AutoreleasingUnsafeMutablePointer` to the original completion handler.
      */
     convenience init?(task: AWSS3TransferUtilityUploadTask, oldProgressPointer: UploadProgressPointer, oldCompletionPointer: UploadCompletionPointer) {

         self.init(attachment: nil) // Actual implementation finds attachment record

         // Re-connect progress handler
         var progressBlock: AWSS3TransferUtilityProgressBlock = self.uploadProgressHandler
         let newProgressPointer = UploadProgressPointer(&progressBlock)
         print("newProgressPointer", newProgressPointer)
         print("newProgressPointer.memory", newProgressPointer.memory) // Throws EXC_BAD_ACCESS KERN_INVALID_ADDRESS
         oldProgressPointer.memory = newProgressPointer.memory

         // Re-connect completion handler
         var completionBlock: AWSS3TransferUtilityUploadCompletionHandlerBlock = self.uploadCompletionHandler
         let newCompletionPointer = UploadCompletionPointer(&completionBlock)
         oldCompletionPointer.memory = newCompletionPointer.memory
     }

     /**
      Handles file upload progress. `AWSS3TransferUtility` calls this repeatedly while the file is
      uploading.

      - parameter task:     The `AWSS3TransferUtilityTask` for the current upload.
      - parameter progress: The `NSProgress` object for the current upload.
      */
     private func uploadProgressHandler(task: AWSS3TransferUtilityTask, progress: NSProgress) {

         // We copy the `completedUnitCount` to operation but it would be nicer if we could just
         // reference the one passed to us instead of having two separate instances
         self.progress.completedUnitCount = progress.completedUnitCount

         // Calculate file transfer rate using an exponential moving average, as per https://stackoverflow.com/a/3841706/171144
         let lastRate = self.transferRate
         let averageRate = Double(progress.completedUnitCount) / (NSDate.timeIntervalSinceReferenceDate() - self.uploadStartedAt!)
         self.transferRate = self.smoothingFactor * lastRate + (1 - self.smoothingFactor) * averageRate;
         progress.setUserInfoObject(self.transferRate, forKey: NSProgressThroughputKey)
     }

     /**
      Handles file upload completion. `AWSS3TransferUtility` calls this when the file has finished
      uploading or is aborted due to an error.

      - parameter task:  The `AWSS3TransferUtilityTask` for the current upload.
      - parameter error: An instance of `NSError` if the upload failed.
      */
     private func uploadCompletionHandler(task: AWSS3TransferUtilityUploadTask, error: NSError?) {

        ...

     }

     ...

 }

为什么指针 memory 引用在创建后立即无效?

作为 iOS 开发的新手并且没有 Obj-C(或其他非内存管理语言)的实际经验,我有点迷茫。如果有人能阐明一些观点,我们将不胜感激。

编辑:

enumerateToAssignBlocksForUploadTask(…) 的 Swift 方法签名

/**
 Assigns progress feedback and completion handler blocks. This method should be called when the app was suspended while the transfer is still happening.

 @param uploadBlocksAssigner   The block for assigning the upload pregree feedback and completion handler blocks.
 @param downloadBlocksAssigner The block for assigning the download pregree feedback and completion handler blocks.
 */
public func enumerateToAssignBlocksForUploadTask(uploadBlocksAssigner: ((AWSS3TransferUtilityUploadTask, AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityTask, NSProgress) -> Void)?>, AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityUploadTask, NSError?) -> Void)?>) -> Void)?, downloadTask downloadBlocksAssigner: ((AWSS3TransferUtilityDownloadTask, AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityTask, NSProgress) -> Void)?>, AutoreleasingUnsafeMutablePointer<(@convention(block) (AWSS3TransferUtilityDownloadTask, NSURL?, NSData?, NSError?) -> Void)?>) -> Void)?)

最佳答案

我认为您不应该需要这些指针中的大部分(或任何)。看看 Swift example code from AWS看看它是否不符合您的要求。

以您现有的方式创建指针在 Swift 中并不安全。这可能比您需要的信息多得多(您不必如此努力地工作),但可能会发生这种情况(在这种情况下,这种解释并不完全正确,但这是可能发生的事情,所以值得了解):

  • 您创建一个指向本地(堆栈)变量的指针 progressBlock
  • 系统发现 progressBlock 不再在范围内的任何其他地方访问。
  • 在允许的情况下,ARC 会销毁 progressBlock
  • 您不安全地访问指向已销毁变量的指针,然后崩溃。

我说这感觉不对,因为还有对闭包的第二个引用无论如何都应该使闭包保持事件状态,但通常,以这种方式使用构造函数创建指针是非常危险的。

(这可能会崩溃,因为您无法打印 @convention(block) 闭包;我从未尝试过这样做。这不是很正常尝试打印的东西。)

无论如何,如果您确实需要做这种事情(而且我认为您不需要),您需要按照以下方式进行:

withUnsafeMutablePointer(to: self.uploadProgressHandler) { newProgressPointer in 
    ... newProgressPointer is safe to use in this block ...
}

但通常,如果您正在转换 ObjC 代码(不是纯 C,而只是 ObjC),并且发现您必须创建很多 Unsafe 对象,您可能正在走走错了路。大多数 ObjC 事物都可以很好地桥接到 Swift,而无需 Unsafe

关于ios - 为什么我刚创建的指针抛出 KERN_INVALID_ADDRESS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41854286/

有关ios - 为什么我刚创建的指针抛出 KERN_INVALID_ADDRESS?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  8. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  9. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  10. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

随机推荐