无法从内存堆栈中释放手势识别器
Unable to deallocate Gesture Recognizer from Memory Stack
我正在尝试在我的 mapView 上实现自定义手势识别器,以防止用户放大或缩小超过由 MKCoordinateSpan 设置的特定阈值。
mapView 的 ViewController 是选项卡栏控制器的一部分,因此每次视图消失时我都会删除 mapView 并出于内存目的重新添加它。
自从我添加了自定义手势识别器后,当视图消失时内存不会被释放。除了从 mapView 中删除手势识别器之外,我还缺少什么?
地图ViewController:
class MapViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
loadMapView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if mapView == nil {
loadMapView()
}
}
override func viewDidDisappear(_ animated:Bool) {
super.viewDidDisappear(animated)
self.applyMapViewMemoryFix()
}
func loadMapView() {
self.edgesForExtendedLayout = []
setMapView()
}
func setMapView() {
if self.mapView == nil {
addMapView()
}
mapView.delegate = self
mapView.mapType = .mutedStandard
mapView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}
func addMapView() {
mapView = MKMapView()
mapView.frame = self.navigationController!.view.bounds
mapView.mapType = MKMapType.standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
self.view.addSubview(mapView)
}
func applyMapViewMemoryFix() {
for recognizer in (self.mapView?.gestureRecognizers)! {
if recognizer is WildCardGestureRecognizer {
self.mapView.removeGestureRecognizer(recognizer)
}
}
self.mapView.showsUserLocation = false
self.mapView.delegate = nil
self.mapView.removeFromSuperview()
self.mapView = nil
}
}
我为手势识别器设置边界的扩展:
extension MapViewController: MKMapViewDelegate {
// View Region Changing
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let northernBorder = 32.741152
let southernBorder = 32.731461
let easternBorder = -117.143622
let westernBorder = -117.157399
var latitude = mapView.region.center.latitude
var longitude = mapView.region.center.longitude
if (mapView.region.center.latitude > northernBorder) {
latitude = northernBorder
}
if (mapView.region.center.latitude < southernBorder) {
latitude = southernBorder
}
if (mapView.region.center.longitude > easternBorder) {
longitude = easternBorder
}
if (mapView.region.center.longitude < westernBorder) {
longitude = westernBorder
}
let tapInterceptor = WildCardGestureRecognizer(target: nil, action: nil)
tapInterceptor.touchesBeganCallback = {_, _ in
mapView.isZoomEnabled = true
}
tapInterceptor.touchesMovedCallback = {_, _ in
if tapInterceptor.scale < 1 {
if (latitude != mapView.region.center.latitude || longitude != mapView.region.center.longitude)
|| ((mapView.region.span.latitudeDelta > (northernBorder - southernBorder) )
|| (mapView.region.span.longitudeDelta > (easternBorder - westernBorder))) {
let span = MKCoordinateSpan.init(latitudeDelta: 0.007, longitudeDelta: 0.007)
if mapView.region.span.latitudeDelta > span.latitudeDelta || mapView.region.span.longitudeDelta > span.longitudeDelta {
mapView.isZoomEnabled = false
} else {
mapView.isZoomEnabled = true
}
}
} else if tapInterceptor.scale > 1 {
let minimumSpan = MKCoordinateSpan.init(latitudeDelta: 0.002, longitudeDelta: 0.002)
if mapView.region.span.latitudeDelta < minimumSpan.latitudeDelta || mapView.region.span.longitudeDelta < minimumSpan.longitudeDelta {
mapView.isZoomEnabled = false
} else {
mapView.isZoomEnabled = true
}
}
}
tapInterceptor.touchesEndedCallback = {_, _ in
mapView.isZoomEnabled = true
}
mapView.addGestureRecognizer(tapInterceptor)
}
}
自定义手势识别器:
class WildCardGestureRecognizer: UIPinchGestureRecognizer {
var touchesBeganCallback: ((Set<UITouch>, UIEvent) -> Void)?
var touchesMovedCallback: ((Set<UITouch>, UIEvent) -> Void)?
var touchesEndedCallback: ((Set<UITouch>, UIEvent) -> Void)?
override init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
self.cancelsTouchesInView = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
touchesBeganCallback?(touches, event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
touchesMovedCallback?(touches, event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
touchesEndedCallback?(touches, event)
}
override func canPrevent(_ preventedGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
override func canBePrevented(by preventingGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
您在此处声明手势时发生内存泄漏
let tapInterceptor = WildCardGestureRecognizer(target: nil, action: nil)
.
.
.
mapView.addGestureRecognizer(tapInterceptor)
在 regionDidChangeAnimated
中,因为它被多次调用,所以随着区域的变化,你会在地图视图中添加很多手势,所以最好创建一个像
这样的实例变量
var tapInterceptor:WildCardGestureRecognizer!
并在函数中添加手势初始化和回调,然后将其调用为 viewDidLoad
同时删除 @IBOutle
@IBOutlet var mapView: MKMapView!
如果你不把它放在情节提要中,我也不认为 remove/add 方式会有所不同,因为 IOS 中对象的重新分配总是不会释放整个参与部分,所以最好让 mapview 只保留 1 个手势,而不是在你 select/deselect 点击
时从你丢失的手势中积累大量泄漏
我正在尝试在我的 mapView 上实现自定义手势识别器,以防止用户放大或缩小超过由 MKCoordinateSpan 设置的特定阈值。
mapView 的 ViewController 是选项卡栏控制器的一部分,因此每次视图消失时我都会删除 mapView 并出于内存目的重新添加它。
自从我添加了自定义手势识别器后,当视图消失时内存不会被释放。除了从 mapView 中删除手势识别器之外,我还缺少什么?
地图ViewController:
class MapViewController: UIViewController, CLLocationManagerDelegate {
@IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
loadMapView()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if mapView == nil {
loadMapView()
}
}
override func viewDidDisappear(_ animated:Bool) {
super.viewDidDisappear(animated)
self.applyMapViewMemoryFix()
}
func loadMapView() {
self.edgesForExtendedLayout = []
setMapView()
}
func setMapView() {
if self.mapView == nil {
addMapView()
}
mapView.delegate = self
mapView.mapType = .mutedStandard
mapView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}
func addMapView() {
mapView = MKMapView()
mapView.frame = self.navigationController!.view.bounds
mapView.mapType = MKMapType.standard
mapView.isZoomEnabled = true
mapView.isScrollEnabled = true
self.view.addSubview(mapView)
}
func applyMapViewMemoryFix() {
for recognizer in (self.mapView?.gestureRecognizers)! {
if recognizer is WildCardGestureRecognizer {
self.mapView.removeGestureRecognizer(recognizer)
}
}
self.mapView.showsUserLocation = false
self.mapView.delegate = nil
self.mapView.removeFromSuperview()
self.mapView = nil
}
}
我为手势识别器设置边界的扩展:
extension MapViewController: MKMapViewDelegate {
// View Region Changing
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let northernBorder = 32.741152
let southernBorder = 32.731461
let easternBorder = -117.143622
let westernBorder = -117.157399
var latitude = mapView.region.center.latitude
var longitude = mapView.region.center.longitude
if (mapView.region.center.latitude > northernBorder) {
latitude = northernBorder
}
if (mapView.region.center.latitude < southernBorder) {
latitude = southernBorder
}
if (mapView.region.center.longitude > easternBorder) {
longitude = easternBorder
}
if (mapView.region.center.longitude < westernBorder) {
longitude = westernBorder
}
let tapInterceptor = WildCardGestureRecognizer(target: nil, action: nil)
tapInterceptor.touchesBeganCallback = {_, _ in
mapView.isZoomEnabled = true
}
tapInterceptor.touchesMovedCallback = {_, _ in
if tapInterceptor.scale < 1 {
if (latitude != mapView.region.center.latitude || longitude != mapView.region.center.longitude)
|| ((mapView.region.span.latitudeDelta > (northernBorder - southernBorder) )
|| (mapView.region.span.longitudeDelta > (easternBorder - westernBorder))) {
let span = MKCoordinateSpan.init(latitudeDelta: 0.007, longitudeDelta: 0.007)
if mapView.region.span.latitudeDelta > span.latitudeDelta || mapView.region.span.longitudeDelta > span.longitudeDelta {
mapView.isZoomEnabled = false
} else {
mapView.isZoomEnabled = true
}
}
} else if tapInterceptor.scale > 1 {
let minimumSpan = MKCoordinateSpan.init(latitudeDelta: 0.002, longitudeDelta: 0.002)
if mapView.region.span.latitudeDelta < minimumSpan.latitudeDelta || mapView.region.span.longitudeDelta < minimumSpan.longitudeDelta {
mapView.isZoomEnabled = false
} else {
mapView.isZoomEnabled = true
}
}
}
tapInterceptor.touchesEndedCallback = {_, _ in
mapView.isZoomEnabled = true
}
mapView.addGestureRecognizer(tapInterceptor)
}
}
自定义手势识别器:
class WildCardGestureRecognizer: UIPinchGestureRecognizer {
var touchesBeganCallback: ((Set<UITouch>, UIEvent) -> Void)?
var touchesMovedCallback: ((Set<UITouch>, UIEvent) -> Void)?
var touchesEndedCallback: ((Set<UITouch>, UIEvent) -> Void)?
override init(target: Any?, action: Selector?) {
super.init(target: target, action: action)
self.cancelsTouchesInView = false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
touchesBeganCallback?(touches, event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
touchesMovedCallback?(touches, event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
touchesEndedCallback?(touches, event)
}
override func canPrevent(_ preventedGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
override func canBePrevented(by preventingGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
}
您在此处声明手势时发生内存泄漏
let tapInterceptor = WildCardGestureRecognizer(target: nil, action: nil)
.
.
.
mapView.addGestureRecognizer(tapInterceptor)
在 regionDidChangeAnimated
中,因为它被多次调用,所以随着区域的变化,你会在地图视图中添加很多手势,所以最好创建一个像
var tapInterceptor:WildCardGestureRecognizer!
并在函数中添加手势初始化和回调,然后将其调用为 viewDidLoad
同时删除 @IBOutle
@IBOutlet var mapView: MKMapView!
如果你不把它放在情节提要中,我也不认为 remove/add 方式会有所不同,因为 IOS 中对象的重新分配总是不会释放整个参与部分,所以最好让 mapview 只保留 1 个手势,而不是在你 select/deselect 点击
时从你丢失的手势中积累大量泄漏