Програмно създаден UITableView с несвойствен източник на данни и делегати

Бих искал да използвам екземпляр на отделен клас като източник на данни и делегат на моя UITableView. Създавам нов екземпляр на класа, който трябва да обработва тази роля и го присвоявам чрез свойствата на моя табличен изглед:

autoMakesTable се дефинира като свойство:

var autoMakesTable : UITableView?

По-надолу:

    func nextBttnTouched(sender: AnyObject) {

    switch self.autoState {

    case .NewOrEdit:

        self.autoState = .YearEntered

        // If the picker wasn't used create a new Auto with the default year value
        self.auto = Auto(year: NSNumber(integerLiteral: self.yearPickerValues.count - 2), make: "", model: "", body: "", trim: "", downPaymentBudget: 0, monthlyPaymentBudget: 0)

        // Prepare the make & model fields
        backgroundFieldView                     = UIView(frame: CGRectMake(0, 0-self.view.frame.size.height/2, self.view.frame.width, self.view.frame.height/2))
        backgroundFieldView!.backgroundColor    = UIColor(red: 251/255, green: 251/255, blue: 251/255, alpha: 1.0)

        let makeField           = UITextField(frame: CGRectMake(self.view.center.x - 100, self.navigationController!.navigationBar.frame.size.height + 34, 200, 30))
        makeField.delegate      = self
        makeField.placeholder   = "Make"
        makeField.addTarget(self, action: Selector("makeTextChanged:"), forControlEvents: UIControlEvents.EditingChanged)

        let modelField = UITextField(frame: CGRectMake(self.view.center.x - 100, makeField.frame.origin.y + makeField.frame.size.height + 8, 200, 30))
        modelField.placeholder = "Model"

        let newAutoMakesTableData = AutoMakesTableData()

        autoMakesTable = UITableView(frame: CGRectMake(makeField.frame.origin.x, makeField.frame.origin.y + makeField.frame.height + 2, makeField.frame.width, 100))
        autoMakesTable!.dataSource    = newAutoMakesTableData
        autoMakesTable!.delegate      = newAutoMakesTableData
        self.backgroundFieldView?.addSubview(self.autoMakesTable!)
        self.autoMakesTable!.reloadData()

        makeField.becomeFirstResponder()

        self.addBorderToTextField([makeField,modelField])

        self.view.addSubview(backgroundFieldView!)

        // Move year picker with animation
        // Update next button location while loading make / model input fields with animation
        UIView.animateWithDuration(0.8, animations: { () -> Void in

            self.newYearPicker?.frame.origin.y = 0
            self.backgroundFieldView!.frame.origin.y = 0
            self.backgroundFieldView!.addSubview(makeField)
            self.backgroundFieldView?.addSubview(modelField)
//self.backgroundFieldView?.addSubview(self.autoMakesTable!)
//                self.autoMakesTable!.reloadData()
        })

    case .YearEntered:
        self.autoState = .MakeModelEntered

    case .MakeModelEntered:
        self.autoState = .DetailsEntered

    case .DetailsEntered:
        self.autoState = .BudgetEntered
    case .BudgetEntered:
        performSegueWithIdentifier("next", sender: self)

    }

}

Добавя се към йерархията на изгледите (виждам непокътнат празен изглед на таблица с няколко реда) по-късно. Също така се опитвам да извикам reloadData() върху него без ефект.

AutoMakesTableData се дефинира така:

import UIKit

class AutoMakesTableData: NSObject, UITableViewDataSource, UITableViewDelegate {

// Auto makes URL
// This should be hosted online so changes can be made w/out having to re-submit the app
let urlForMakes = NSBundle.mainBundle().URLForResource("makes", withExtension: "JSON")
var autoMakes : [String]?

override init() {
    super.init()
    self.populateAutoMakesFromURL(urlForMakes!)

}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 1
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let aCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)

    print("cell created")

    return aCell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    print("selected row")
}

private func populateAutoMakesFromURL(url: NSURL) {

    do {

        let jsonAutoMakesData = try NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingMappedIfSafe)

        do {

            let jsonAutoMakes = try NSJSONSerialization.JSONObjectWithData(jsonAutoMakesData, options: NSJSONReadingOptions.MutableContainers) as! [String:[String]]

            self.autoMakes = jsonAutoMakes["makes"]
            // Sort array alphabetically
            self.autoMakes?.sortInPlace(<)

        } catch let error as NSError {

            print(error.userInfo.debugDescription)
        }

    } catch let error as NSError {

        print(error.userInfo.debugDescription)

    }

}

}

Мога да осъществя достъп до свойството на масива autoMakes в този екземпляр без проблем.

Изгледът на таблицата се показва, но нито един от изразите за печат не се задейства (за клетка в индексния път или за избран ред).


person Fred Faust    schedule 16.09.2015    source източник


Отговори (1)


Не виждам никъде, където да инсталирате табличния изглед в йерархията на изгледа. Ако не инсталирате контролера на изгледа в йерархията на активния изглед, той няма да направи нищо.

Това е моето предположение какво не е наред.

Добре е отделен обект да служи като източник на данни/делегат на контролера на изгледа.

РЕДАКТИРАНЕ:

Въз основа на дискусията под този отговор се оказва, че проблемът е бил, че OP създава своя AutoMakesTableData в метод и го присвоява на локална променлива, но не запазва никакви други силни препратки към него.

Когато методът, който създаде обекта AutoMakesTableData, се върна, локалната променлива излезе извън обхвата, нямаше повече силни препратки към обекта и той беше освободен. Свойствата делегат и dataSource на табличен изглед са слаби, така че те се нулират, когато обектът бъде освободен. Изгледът на таблицата вече няма източник на данни, така че нищо не се случва.

person Duncan C    schedule 16.09.2015
comment
Съжаляваме, той е инсталиран по-късно във файла, актуализирайки въпроса сега. - person Fred Faust; 16.09.2015
comment
Също така покажете контекста, в който създавате променливата newAutoMakesTableData. Някой се нуждае от силна препратка към този обект или той ще бъде освободен. Вашата публикация го кара да изглежда като локална променлива, което означава, че веднага щом излезе извън обхвата, тя се освобождава и свойствата на източника на данни и делегата на изгледа на таблицата ще бъдат зададени на нула (те са слаби препратки). - person Duncan C; 16.09.2015
comment
Добавих контекста на кода. Опитах го и извън затварянето на анимацията (точно над него) без ефект. - person Fred Faust; 16.09.2015
comment
Покажете целия метод, който дефинира newAutoMakesTableData, и ни кажете в какъв клас е дефиниран този метод. Изглежда ми, че редът let newAutoMakesTableData = AutoMakesTableData() дефинира newAutoMakesTableData като локална променлива, но никога не го присвоявате на променлива със силно свойство или екземпляр. Ако това е вярно, веднага щом този метод се върне, newAutoMakesTableData ще бъде освободен. - person Duncan C; 16.09.2015
comment
Точно така, благодаря. Веднага след като направих свойство на екземпляр за newAutoMakesTableData и присвоих свойствата на tableview на това свойство, всичко заработи. - person Fred Faust; 16.09.2015
comment
Мда. Изводът е, че всеки обект, в който искате да живеете, трябва да бъде собственост на някой, който остава наоколо. Ако създадете обект и единствената силна препратка е локална променлива, обектът изчезва веднага щом напуснете обхвата на тази локална променлива и силната препратка се освобождава. - person Duncan C; 16.09.2015
comment
Мерси - щом научих що за силен реф. цикъл винаги съм се опитвал да работя отвътре навън в страх да не имам два обекта, заключени в съществуването, но това има целия смисъл на света. - person Fred Faust; 16.09.2015
comment
Делегатите са слаби референции, за да се избегнат референтни цикли. В противен случай контролерът на изглед ще притежава изглед на таблица, а изгледът на таблица ще притежава своя делегат и ще получите цикъл на запазване. Основното правило е, че в ситуация, в която 2 обекта сочат един към друг, една от тези препратки трябва да е слаба. - person Duncan C; 16.09.2015