从子视图中删除UIView会清除内存吗?

时间:2018-04-23 11:52:14

标签: ios swift swift4

我正在尝试添加一个包含Graph的UIView(ViewB),它在superview(ViewA)中使用了大量内存。 UIView是" Window System"因此,当用户单击ViewA中的按钮时,将重新计算数据并在ViewB中重新显示布局。

我的第一种方法是将ViewB添加到ViewA中,并插入一个标签,我可以将其从ViewA中删除并重新添加。我在开始时想到,当从超级视图中删除视图并且分配给nil的值时,将释放内存。事实证明,ViewB重新添加,并且由于内存问题,应用程序最终崩溃。

这是我的代码。我有什么问题吗?

    func updateSoundWaveView(){

    if readFile.audioBuffer?.frameCapacity != 0{

        //NEW Setup
        if soundWaveContainer.viewWithTag(soundViewTag) != nil{
            waveView?.removeFromSuperview()
            waveView = nil
        }

        let totalWidth = windowTotalMinutes * 60 * 10
        print("\(logClassName): total Sound View Width = \(totalWidth)")

        waveView = SoundWaveView.init(withReadFile: readFile,
                                      containerFrame: CGRect(x: 0,
                                                             y: 0,
                                                             width: soundWaveContainer.frame.width,
                                                             height:  soundWaveContainer.frame.size.height),
                                      soundFrame: CGRect.init(x: 0,
                                                              y: 0,
                                                              width: CGFloat(totalWidth),
                                                              height: soundWaveContainer.frame.size.height)
        )

        /* Set UP Time Representation */
        waveView?.fractionsSeconds = 10
        waveView?.startMarkerPosition = startPosition - windowStart
        waveView?.endMarkerPosition = endPosition - windowStart
        waveView?.durationRepesentation = Int32(windowTotalMinutes) * 60 * 1000

        waveView?.tag = soundViewTag
        soundWaveContainer.addSubview(waveView!)

        /* Set Up Callbacks */
        waveView?.onTimeSelected = {newPosition in
            DispatchQueue.main.async {
                self.waveViewTimeSelected(newPosition: newPosition)
            }
        }
        waveView?.onMarkerSelected = {
            DispatchQueue.main.async {
                self.waveViewMarkerSelected()
            }
        }

        /* Not YET */
        //waveView?.drawSoundWave(fromSample: fromSample, toSample: toSample)

    }

}

EDIT。

值得一提的是,waveView是我的ViewB,它是一个自定义类,并被添加到soundViewContainer ViewA和IBOutlet中。

class SoundWaveView: UIView, UIGestureRecognizerDelegate {

//MARK: - Variables
//MARK: Constants

//MARK: Vars
var onTimeSelected:((CGFloat)->())?

var readFile:ReadFile

/*** UI ***/
var scrollView:UIScrollView = UIScrollView()
var waveView:UIView
var markerLeft:MarkerView?
var markerRight:MarkerView?

lazy private var middleY:CGFloat = { return frame.size.height/2 }()
var currentMarkerSelected = -1
var onMarkerSelected:(()->())?
//override class var layerClass: AnyClass { return CATiledLayer.self }


/*** Sound Representation ***/
/** View Time Representation **/
var fractionsSeconds:Int32 = 10

// in MiliSeconds
var startMarkerPosition:Int32 = 0
var startMarkerPositionInView:CGFloat = 0
var endMarkerPosition:Int32 = 0
var endMarkerPositionInView:CGFloat = 0

private var totalMinutes:CGFloat{
    get{
        return CGFloat(durationRepesentation)/60000
    }
}
private var totalSecond:CGFloat{
    return CGFloat(durationRepesentation)/1000
}
var durationRepesentation:Int32 = 0



/** WaveView representation **/
var samplesSeconds:Int = 10
var lineWidth:CGFloat = 0.20
var sampleSpace:CGFloat = 0.10



//MARK: - Lifecycle Methods
init(withReadFile readFile: ReadFile, containerFrame:CGRect, soundFrame: CGRect) {

    self.readFile = readFile

    waveView = UIView.init(frame: soundFrame)
    waveView.backgroundColor = UIColor.darkGray

    scrollView = UIScrollView(frame: containerFrame)
    scrollView.addSubview(waveView)
    scrollView.contentSize = waveView.bounds.size

    super.init(frame: containerFrame)


}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func didMoveToSuperview() {
    setupView()
}



//MARK: - Helpers
private func setupView(){
    print("\(logClassName): setupView")

    addSubview(scrollView)

    startMarkerPositionInView = transformToGraphCordinates(position: startMarkerPosition)
    markerLeft = MarkerView(withView: self, color: .white, width: 2, title:"Start", position:startMarkerPositionInView, direction:.right)
    waveView.addSubview(markerLeft!)
    markerLeft?.triangleView?.tag = 0
    markerLeft?.triangleView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(markerViewTouched)))


    endMarkerPositionInView = transformToGraphCordinates(position: endMarkerPosition)
    markerRight = MarkerView(withView: self, color: .white, width: 2, title:"End", position:endMarkerPositionInView, direction:.left)
    waveView.addSubview(markerRight!)
    markerRight?.triangleView?.tag = 1
    markerRight?.triangleView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(markerViewTouched)))

    print("\(logClassName): TEST -> endPoint = \(endMarkerPositionInView) VS View Width = \(waveView.frame.width)")

    /** Add Pan Gesture **/
    self.isUserInteractionEnabled = true
    waveView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(viewTouched)))


}

private func transformToGraphCordinates(position:Int32)->CGFloat{

    let percentatgeEnd = ((totalMinutes * CGFloat(position)) / CGFloat(durationRepesentation))
    let endPositionInGraph:CGFloat = (percentatgeEnd * (totalMinutes * 60 * CGFloat(fractionsSeconds))) / (totalMinutes)

    return endPositionInGraph
}

@objc func markerViewTouched(tapGestureRecogniser:UITapGestureRecognizer){

    let tagView = tapGestureRecogniser.view!.tag
    print("\(logClassName): markerViewTouched \(tagView)")

    selectMarker(at: tagView)

}

@objc func viewTouched(tapGestureRecognizer:UITapGestureRecognizer){

    if currentMarkerSelected != -1{

        let tappedPosition = tapGestureRecognizer.location(in: tapGestureRecognizer.view)
        print("\(logClassName): viewTouched \(tappedPosition)")

        if currentMarkerSelected == 0{
            startMarkerPosition = Int32((tappedPosition.x / CGFloat(fractionsSeconds)) * 1000)
            startMarkerPositionInView = tappedPosition.x

            if startMarkerPositionInView < endMarkerPositionInView{
                markerLeft?.translateView(toPosition: startMarkerPositionInView)
                onTimeSelected?(tappedPosition.x)
            }

        }
        else if currentMarkerSelected == 1{
            endMarkerPosition = Int32((tappedPosition.x / CGFloat(fractionsSeconds)) * 1000)
            endMarkerPositionInView = tappedPosition.x

            if endMarkerPositionInView > startMarkerPositionInView{
                markerRight?.translateView(toPosition: endMarkerPositionInView)
                onTimeSelected?(tappedPosition.x)
            }

        }

    }

}
//MARK: - Methods
func drawSoundWave(fromSample:Int64, toSample:Int64){
    // Drawing code
    print("\(logClassName): Drawing from = \(fromSample) to \(toSample)")

    let soundPath = UIBezierPath()
    soundPath.lineWidth = lineWidth
    soundPath.move(to: CGPoint(x:0.0 , y: middleY))

    let testTo = Int64(toSample)


    let sequence = stride(from: fromSample, to: testTo, by: 4800)


    var testIndex = 0
    for element in sequence {

        let newSample:CGFloat? = CGFloat(readFile.audioBuffer?.floatChannelData?.pointee.advanced(by: Int(element)).pointee ?? 0)

        /** Continuous View **/
        let nextPoint = CGPoint(x: soundPath.currentPoint.x + sampleSpace,
                                y: middleY - ((newSample ?? 0) * 100) - 1.0)

        soundPath.addLine(to: nextPoint)
        soundPath.move(to: nextPoint)

        testIndex += 1

    }

    let trackLayer = CAShapeLayer()
    trackLayer.path = soundPath.cgPath

    self.layer.addSublayer(trackLayer)

    trackLayer.strokeColor = UIColor.red.cgColor
    trackLayer.lineWidth = 0.10
    trackLayer.fillColor = UIColor.green.cgColor
    trackLayer.lineCap = kCALineCapRound

}

func moveMarker(withId id:Int, toPosition position:Int32){

    switch id {
    case 0:
        startMarkerPosition = position
        startMarkerPositionInView = transformToGraphCordinates(position: position)
        markerLeft?.translateView(toPosition: startMarkerPositionInView)
    case 1:
        endMarkerPosition = position
        endMarkerPositionInView = transformToGraphCordinates(position: position)
        markerRight?.translateView(toPosition: endMarkerPositionInView)
    default:
        break
    }

}

func selectMarker(at tagView:Int){

    /** Left Marker **/
    if tagView == 0{

        if currentMarkerSelected == -1{
            currentMarkerSelected = 0
            markerLeft!.isSelected = true
        }
        else if currentMarkerSelected == 1{
            currentMarkerSelected = 0
            markerLeft?.isSelected = true
            markerRight?.isSelected = false
        }
        else if currentMarkerSelected == 0{
            currentMarkerSelected = -1
            markerLeft?.isSelected = false
        }

    }
    else if tagView == 1{

        if currentMarkerSelected == -1{
            currentMarkerSelected = 1
            markerRight!.isSelected = true
        }
        else if currentMarkerSelected == 0{
            currentMarkerSelected = 1
            markerRight?.isSelected = true
            markerLeft?.isSelected = false
        }
        else if currentMarkerSelected == 1{
            currentMarkerSelected = -1
            markerRight?.isSelected = false
        }

    }

    onMarkerSelected?()

}

}

对函数进行8次调用后,使用的内存为1 GB。然后app崩溃了...... enter image description here

2 个答案:

答案 0 :(得分:1)

  1. 此处不要使用视图标记。您只需检查superview的{​​{1}}即可知道是否需要将其删除。像这样:

    waveView
  2. 请勿使用if waveView?.superview != nil { waveView?.removeFromSuperview() waveView = nil } var。

  3. weak初始化为MarkerView,如果它以某种方式保留,那么您将有一个保留周期,因为SoundWaveView实例有2个SoundWaveView实例作为其'子视图。
  4. 另外,您可以检查Xcode内存图,这是查找内存泄漏的好方法。

答案 1 :(得分:1)

waveView?.onTimeSelected = {newPosition in
    DispatchQueue.main.async {
        self.waveViewTimeSelected(newPosition: newPosition)
    }
}
waveView?.onMarkerSelected = {
    DispatchQueue.main.async {
        self.waveViewMarkerSelected()
    }
}

不应该使用弱自我?

之类的东西
waveView?.onTimeSelected = {[weak self] _, newPosition in
    DispatchQueue.main.async {
        self?.waveViewTimeSelected(newPosition: newPosition)
    }
}
waveView?.onMarkerSelected = {
    DispatchQueue.main.async { [weak self] _ in
        self.waveViewMarkerSelected()
    }
}

如果我没有错,不使用弱自我创建一个保留周期,你的观点将被解除分配,而#34; self&#34;仍然存在。