UIWindow防录屏/截屏实现中的循环引用及动态取消安全状态黑屏问题解决方案咨询
嘿,我来帮你搞定这两个棘手的问题!你用UITextField的secure特性实现防录屏/截屏的思路其实挺巧妙的,但原代码里的层级嵌套确实踩了两个大坑——循环引用和取消时黑屏,我给你梳理下问题根源和修复方案:
一、循环引用的问题根源与修复
原代码里这两行是罪魁祸首:
self.layer.superlayer?.addSublayer(field.layer) field.layer.sublayers?.last?.addSublayer(self.layer)
你把window的layer嵌套到了UITextField的私有secure层下面,同时UITextField又是window的子视图,它的layer原本就依赖window的layer作为父层,这就形成了循环引用链:window的layer的父层是field的layer的子层,而field的layer的父层又是window的layer——这种互相嵌套的层级会导致内存无法正常释放,轻则内存泄漏,重则直接崩溃。
修复思路很简单:不要嵌套window的layer,我们只需要把UITextField的那个私有secure层单独抽出来,放到window的layer的同级层级(也就是window所在场景的根layer),让它覆盖整个window即可。这样既利用了secure层的防录屏特性,又不会形成循环引用。
二、动态取消安全状态时黑屏的问题修复
原代码里移除UITextField时黑屏,是因为你之前把window的layer嵌套到了field的layer下面,移除field的时候,它的整个layer树都会被销毁,window的layer也跟着被带走了,自然就黑屏了。
修复思路:我们根本不需要把UITextField添加为window的子视图,也不需要嵌套window的layer——只需要提取它的secure层,单独管理这个层的生命周期。取消安全状态时,只需要移除这个secure层就行,window的layer完全不受影响。
完整修复后的代码
import UIKit private var secureLayerKey: UInt8 = 0 extension UIWindow { // 用关联对象保存secure层的引用,方便后续移除 private var secureOverlayLayer: CALayer? { get { return objc_getAssociatedObject(self, &secureLayerKey) as? CALayer } set { objc_setAssociatedObject(self, &secureLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func makeSecure() { // 避免重复添加secure层 guard secureOverlayLayer == nil else { return } // 只创建UITextField来获取它的secure层,不需要添加到视图层级 let secureField = UITextField() secureField.isSecureTextEntry = true secureField.bounds = self.bounds // 提取UITextField的私有secure层(就是它layer的最后一个子层) guard let secureSublayer = secureField.layer.sublayers?.last else { return } // 设置secure层覆盖整个window,关闭交互避免挡住用户操作 secureSublayer.frame = self.bounds secureSublayer.isUserInteractionEnabled = false // 把secure层添加到window的父层级(场景根layer),和window的layer同级 self.layer.superlayer?.addSublayer(secureSublayer) // 保存引用,后面取消安全状态时要用 self.secureOverlayLayer = secureSublayer } func makeUnsecure() { // 移除之前添加的secure层,恢复正常状态 guard let secureLayer = secureOverlayLayer else { return } secureLayer.removeFromSuperlayer() secureOverlayLayer = nil } }
这个方案为啥能满足你的需求?
- 彻底解决循环引用:所有layer都是单向层级,没有互相嵌套的情况,内存可以正常释放
- 不需要动态隐藏/显示视图:secure层一直存在,正常状态下完全不影响用户操作(关闭了交互,无内容时是透明的),只有在录屏/截屏时会自动触发模糊效果
- 取消安全状态不会黑屏:移除的只是单独的secure层,window的layer完全不受影响,自然不会出现黑屏
内容来源于stack exchange

