我的应用有四个视图控制器(VC'):
1)主页:在SKView上有一个SpriteKit动画。在此VC上滑动可让用户进入创作VC(下一个)。
2)创作:有一个菜单(一个UITableView)。该菜单允许用户访问ViewGames和Store(下方)。
3) ViewGames :包含一个UICollectionView和一个带有关闭按钮的导航栏。这一次呈现单个UICollectionViewCell,并允许用户滑动以进入下一个单元格。每个单元格在SKView上都有一个SpriteKit动画,并且还有三个按钮。
4)商店:拥有一个应用内购买商店,其UI实现为UITableView。出于本讨论的目的,我使用的唯一功能是SKProductsRequest来获取在表格视图中显示的产品列表。
问题:在ViewGames VC中,在某些情况下,集合视图中第二个和后续单元格的UI会慢慢运行非常。例如,比正常慢约10倍。 SKView中的动画非常慢。四个按钮(集合视图单元格中的三个按钮,导航栏中的一个按钮)操作非常缓慢。通常他们根本没有回应,你必须多次点击它们。转到下一个单元格的滑动响应类似 - 如果有的话,慢慢地响应。 (如果我使用滑动返回第一个单元格,第一个单元格也会有类似的响应,但最初它没有表现出这个问题。)
重现问题。好消息是,在我的应用中,重现这个问题是非常一致的。以下是产生它的原因:
启动应用>滑动即可转到“创作”>使用菜单转到商店> 退出存储以返回到创作>退出创作回到主页>走 到创作>去查看游戏。
其他一些方面:
A)如果我退出View Games,返回到Authoring,然后重新进入View Games,这个问题是一样的。
B)该应用程序的其他任何部分都没有表现出这种UI响应的缓慢。
C)如果在View Games VC中获得此行为后,我现在将ViewGames退回到Authoring,重新进入Store,返回到Authoring,然后返回ViewGames,问题就消失了。
D)此问题仅在iOS9,iOS9.1,iOS9.2(测试版)上展示。它不会发生在iOS8.4上。 (全部在物理设备上运行;我还没有尝试过模拟器)。我最初使用Xcode 7.0.1,但现在使用的是Xcode 7.2 beta,问题仍然存在。我的应用针对iOS8及以上版本。
E)如果我启动应用程序,然后转到创作,然后转到ViewGames,这个问题就不会发生。
问题:什么可以让UI的一部分运行缓慢,但只是暂时的?
到目前为止探索的途径:
(i)我在时间分析工具中查看了这个应用程序,但看不到任何看起来像是在吸收时间的东西。
(ii)只有应用程序的一部分正在进行网络交互,这就是商店。产品获取成功,并显示该信息。
(iii)我现在最好的猜测是这与内存使用有关。出现这些症状时,似乎从Authoring UICollectionView的单元格1到单元格2使用的RAM数量至少有所增加(在问题出现的情况下为0.4到0.9MB;在出现问题的情况下为0.3MB)没有出现。)
(iv)在应用程序的开发历史中,当我准备将v1.0提交给Apple时,我有一个内存泄漏,表现出一些这些症状。但是,据我记忆,内存泄漏只会影响SpriteKit动画,影响所有 SpriteKit动画(在Home和Authoring VC上),并且不是临时。您必须重新启动应用才能绕过它。
(v)我使用Instruments / Leaks / Allocations看了很多应用程序。有一些漏洞,但它们似乎来自Apple框架,而不是我的。
(vi)我在dealloc / deinit方法中放置了断点和日志消息,并且所有主要类似乎都在解除分配(例如,VC'以及集合视图和它&#39 ; s细胞)。
Update1 :11/4/15; 3:47 pm MST:问题与ViewGames SpriteKit动画无关。我刚刚在ViewGames UICollectionViewCell中禁用了动画,问题仍然存在。滑动和按钮按下响应仍然会出现迟缓。当然,细胞仍然有SKView / SKScene。
Update2 :11/4/15; MST:我刚刚在商店中禁用了产品获取(使用SKProductsFetch)。 问题消失了!! 显着缩小问题范围!
Update3 :11/4/15;下午6:10 MST:随着产品获取到位,但SKProductsFetch对象的委托设置为nil,问题不会发生!还需要注意一个完成处理程序(称为fetchProductsCompletion) )这是我班级建设的一部分也被定为零。
Update4 :11/4/15;下午6:10 MST:产品获取到位,并且SKProductsFetch的非零委托,但fetchProductsCompletion设置为nil,问题不会发生!
答案 0 :(得分:0)
如果你想彻底冻结应用程序,你可以使用sleep(amountTime) 或者使用runAfterDelay(amountTime){}来延迟。
答案 1 :(得分:0)
我找到了解决问题的方法。我不知道它为什么会起作用。但是,我会发布一些代码来提供上下文。
这是我在商店中用来获取产品的方法:
Set-ADObject -Identity $UserTemplate.objectGuid -Replace @{pKIExpirationPeriod = $NewExpirationPeriod}
商店代理方法(我的构造)如下所示:
// Not calling this from init, so that we can make sure there is a network connection, and only give a single error/alert if there is no network connection.
private func initiateProductFetch(done:(success:Bool)->()) {
let productIds = self.productsInfo!.productIds()
if nil == productIds {
Assert.badMojo(alwaysPrintThisString: "No product Ids!")
}
/* Bug #C34, #C39.
10/30/15; I was having a memory retention issue in regards to the fetchProducts completion handler. The SMIAPStore instance wasn't getting deallocated until the *next* time the store was presented because the store delegate was retaining a reference to the completion handler, which had a strong reference to self (SMIAPStore).
At first I tried to resolve this by having [unowned self] in the following, but that fails with an exception. Not sure why. And having [weak self] also causes self! to fail-- "fatal error: unexpectedly found nil while unwrapping an Optional value". Why?
The only way I've found to work around this is to have a selfCopy as below, which is nil'ed out in the completion handler. Seems really clumsy. For consistency sake, and safety sake, I'm also using this technique in the other self.storeDelegate usages in this class.
*/
var selfCopy:SMIAPStore? = self
self.storeDelegate?.fetchProducts(productIds!) {
(products:[SKProduct]?, error:NSError?) in
Log.msg("\(products)")
if (products != nil && products!.count > 0 && nil == error) {
// No error.
selfCopy!.productsInfo!.products = products
Log.msg("Done fetching products")
done(success:true)
}
else {
// Show an error. The VC will not be displayed by now. Returning the error with the done call back will not allow it to be displayed.
// It seems possible the products are empty and error is nil (at least this occurs in my debug case above).
var message = ""
if error != nil {
message = error!.localizedDescription
}
let alert = UIAlertView(title: "Couldn't get product information from Apple!", message: message, delegate: nil, cancelButtonTitle: SMUIMessages.session().OkMsg())
selfCopy!.userMessageDetails = UserMessage.session().showAlert(alert, ofType: UserMessageType.Error) { buttonNumber in
done(success:false)
}
}
selfCopy = nil
}
}
这是SKProductsRequest的委托方法:
// Call this first to initiate the fetch of the SKProduct info from Apple
public func fetchProducts(productIdentifiers:[String], done: (products:[SKProduct]?, error:NSError?) ->()) {
self.requestDelegateUseType = .ProductsRequest
self.productsRequest = SKProductsRequest(productIdentifiers: Set(productIdentifiers))
self.productsRequest!.delegate = self
self.fetchProductsCompletion = done
self.productsRequest!.start()
}
上面给出的代码确实存在问题。当我在使用fetchProductsCompletion处理程序后将其设置为nil时,问题就会消失。关于为什么的任何想法?