简体   繁体   中英

Swift arrays/collections memory management

I've spent over a week reading various articles online & StackOverflow Q&A on memory management of Swift arrays within UIViewController , I'm still getting crashes in my app. I hope you can help.

I have a parent class controller and multiple subclasses (simplified using examples) that inherit from the parent class, like this.

class ParentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    lazy var results: [AnyObject] = [AnyObject]()  //can contain various classes, such as Product, User, etc.

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return results.count
    }

        ...       
}


class SubViewController: ParentViewController {

   ... loads results Array via REST API, then then appends to array using 
   results.append(Product())

}

Randomly, I'd get crashes like this:

EXC_BAD_ACCESS KERN_INVALID_ADDRESS at 0x0000000a1abb4b17
Thread : Crashed: com.apple.main-thread
0  libobjc.A.dylib                0x000000019478fe10 cache_getImp + 16
1  libobjc.A.dylib                0x0000000194784a6c lookUpImpOrForward + 540
2  libobjc.A.dylib                0x000000019478fdb8 _objc_msgSend_uncached_impcache + 56
3  libswiftCore.dylib             0x00000001004d4e40 _ZN5swift12metadataimpl14ValueWitnessesINS0_17ObjCRetainableBoxEE18initializeWithCopyEPNS_11OpaqueValueES5_PKNS_8MetadataE + 24
4  libswiftCore.dylib             0x000000010036e3cc _TFSa6appendU__fRGSaQ__FQ_T_ + 92

Based on my understanding, I believe it's because results is no longer in memory by the time results.append() is called.

What's the best practice to instantiate arrays/collections in UIViewController, especially when there are subclasses? Here are some of my thoughts, any feedback would be appreciated.

1.Use lazy in parent class. This is the base case and causes crashes

lazy var results: [AnyObject] = [AnyObject]()

2.Init in SubViewController , this still causes crashes, also, should I deallocate the memory by using deinit() ?

in ParentViewController
var results: [AnyObject]

in SubViewController
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.results = [Product]()
    }

3.Make results optional in parent class, this would require all methods in parent class to check for optionals. Should I initialize the array in init in the subclass or in the viewDidLoad() ? Also, should I deinit results? to nil in parent class?

in ParentViewController
var results: [AnyObject]?

deinit {
    results = nil
}

in SubViewController
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.results = [Product]()
    }

Any suggestions would be appreciated!

I don't know what is the life time and relationship of your view controllers, but I one way to do this reliably is to place the "results" array to your AppDelegate. This is guaranteed to exists when you work with your view controllers and is accessible from them.

let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.results.append(...)

Are you adding the result fetched from the rest api in the main thread? If not try this:

dispatch_async(dispatch_get_main_queue(), {
   results.append(objectsFromApi)
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM