我正在尝试添加一个包含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?()
}
}
答案 0 :(得分:1)
此处不要使用视图标记。您只需检查superview
的{{1}}即可知道是否需要将其删除。像这样:
waveView
请勿使用if waveView?.superview != nil {
waveView?.removeFromSuperview()
waveView = nil
}
var。
weak
初始化为MarkerView
,如果它以某种方式保留,那么您将有一个保留周期,因为SoundWaveView
实例有2个SoundWaveView
实例作为其'子视图。另外,您可以检查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;仍然存在。