Objective-C: проблемы с доступом к объектам в других UIViewControllers - PullRequest
0 голосов
/ 03 апреля 2012

Итак, у меня есть Custom UITableViewCell, который содержит ссылку на содержащий его контроллер представления (VC, в котором есть его таблица).

//  MyCell.h
#import <UIKit/UIKit.h>
#import "RootViewController.h"

@interface MyCell : UITableViewCell

@property (nonatomic, strong) IBOutlet RootViewController *rootViewController;

-(IBAction)checkBoxClicked:(UIButton*)sender;


//  MyCell.m
@implementation MyCell

@synthesize rootViewController = _rootViewController;

-(IBAction)checkBoxClicked:(UIButton*)sender
{
    [self setCheckBoxChecked:!_checkBoxChecked];
    [_rootViewController refreshVisibleViewForCellTagged:self.tag];
}

В моей ячейке есть кнопка, которая меняет переменную и затем вызывает функцию в моем rootViewController.Однако этот метод фактически вызывается, когда я пытаюсь получить доступ к любому объекту в RootViewController внутри метода refreshVisibleViewForCellTagged. Они имеют значение «0x0» / nil;

//  RootViewController.h
@interface RootViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) IBOutlet UITableView *myTableView;


//  RootViewController.m
- (void) refreshVisibleViewForCellTagged:(NSInteger)cellTag
{
    UITableView *tableView = self.myTableView; // nil
    NSIndexPath *indexPath = [self.myTableView indexPathForSelectedRow]; // nil
    MyCell *selectedCell = (MyCell*)[self.myTableView cellForRowAtIndexPath:indexPath]; // nil

    if (selectedCell.tag == cellTag) {
        NSLog(@"Refresh one way.");
    } else {
        NSLog(@"Do something else.");
    }
}

Может кто-нибудь пролить свет на то, почему я не могу получить доступкакие-либо объекты / переменные в RootController из метода 'refreshVisibleViewForCellTagged'?

Пожалуйста и спасибо!

** Мой большой вопрос Почему я не могу получить доступлюбые объекты при вызове метода в контроллере представления из другого контроллера представления.Есть некоторая великая правда программирования, о которой я не знаю здесь, это проблема с разрешениями?Я не использую @class (прямая классификация) в этом случае.

Ответы [ 5 ]

2 голосов
/ 03 апреля 2012

Как сказал @trojanfoe, делегирование - лучший способ сделать это. Вместо #import "RootViewController.h" лучше принять делегирование. Потому что UITableViewCell является дочерним, а RootViewController является родительским представлением. Вы не хотите, чтобы ребенок говорил напрямую с родителем.

  • Для принятия делегации:

  • в MyCell.h файле

  • удалить #import "RootViewController.h".

  • изменить MyCell.h следующим образом:

@protocol MyCellDelegate; // if you need to have forward declaration

@interface MyCell : UITableViewCell
// @property (nonatomic, strong) IBOutlet RootViewController *rootViewController;
@property (nonatomic) id <MyCellDelegate> delegate;
@end

@protocol MyCellDelegate <NSObject>
- (void)refreshVisibleViewForCellTagged:(NSInteger)cellTag;
@end
  • в MyCell.m.
@synthesize delegate;

-(IBAction)checkBoxClicked:(UIButton*)sender {
     [self setCheckBoxChecked:!_checkBoxChecked];
     //[_rootViewController refreshVisibleViewForCellTagged:self.tag];
     [self.delegate refreshVisibleViewForCellTagged:self.tag];
}
  • в RootViewController.h принять делегацию MyCell
#import "MyCell.h"

@interface RootViewController : UIViewController <MyCellDelegate>
  • в RootViewController.m.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      UITableViewCell *cell = // your implementation
      //assuming all your cells are of MyCell kind
      // set RootViewController as the delegate of each cell
      ((MyCell *)cell).delegate = self;

      return cell;
}
  • реализовать метод делегата в RootViewController.m.
- (void)refreshVisibleViewForCellTagged:(NSInteger)cellTag {
     // whatever you have
}

P.S. Приведенные выше коды приведены для иллюстрации. Я не управлял ими. Если какая-то часть не работает, дайте мне знать, и я пересмотрю ее.

Причина, по которой эти объекты в RootViewController равны нулю при вызове, заключается в том, что вы не обращаетесь к одному и тому же экземпляру RootViewController. Это другой (новый) экземпляр и, следовательно, все объекты равны нулю.

2 голосов
/ 03 апреля 2012

Не обращайте внимания на тот факт, что контроллеры представления даже участвуют. То, что у вас есть, это ОБЪЕКТЫ, связанные друг с другом определенным образом. Доступ к данным в другом контроллере представления ничем не отличается от доступа к данным в любом другом объекте. В контроллерах представления нет «магии», за исключением нескольких стандартизированных соединений с другими объектами.

2 голосов
/ 03 апреля 2012

ИМХО, это плохой дизайн. Для начала, ваша ячейка не должна нуждаться в ссылке на контроллер представления, в котором находится таблица, в которой она находится (прочитайте это дважды, это едва ли имеет смысл только потому, что сама идея этого сбивает с толку). У вас есть сильная ссылка на этот контроллер представления. Так что же происходит, когда ОС пытается освободить ваш контроллер представления? Он никогда не сможет этого сделать, потому что ячейка табличного представления является сильной ссылкой на нее, сохраняя количество сохраняемых данных равным 1. Та же ситуация сохраняется для ячейки. Вы рискуете столкнуться с циклом сохранения здесь. Как правило, взгляды детей должны иметь слабые ссылки на своих родителей.

Но это даже не настоящие отношения родителей и детей. Вместо этого я бы предложил такой подход, который происходит в вашем контроллере представления, который содержит представление таблицы:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Assuming you set a reuse identifier "cellId" in the nib for your table view cell...
    MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:@"cellId"];
    if (!cell) {
        // If you didn't get a valid cell reference back, unload a cell from the nib
        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:nil options:nil];
        for (id obj in nibArray) {
            if ([obj isMemberOfClass:[MyCell class]]) {
                // Assign cell to obj, and add a target action for the checkmark
                cell = (MyCell *)obj;
                [cell.checkMarkButton addTarget:self action:@selector(checkPressed:) forControlEvents:whateverEventYouWant];
                break;
            }
        }
     }
     // Set the tag of the cell here, since we may get a different cell back from the reuse queue
     cell.checkMarkButton.tag = indexPath.row;

     return cell;
}

Теперь настройте метод нажатия кнопки галочки

- (void)checkPressed:(id)sender {
    UIButton *checkmark = (UIButton *)sender;

    // This will give you the row of the checked button
    int checkedCellRow = checkmark.tag;

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:checkedCellRow inSection:0];

    // Now you can grab a reference to that cell if you need to
    MyCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
}

Таким образом, вы сохраняете все, что связано с контроллером, в вашем классе контроллера (то есть, как обрабатывать нажатие кнопки галочки), и вам не нужно иметь дело с этой глупостью обращения к контроллеру представления вашей ячейки. таблица.

РЕДАКТИРОВАТЬ: Полагаю, я также должен помочь ответить на ваши вопросы ... Прежде всего, если вы говорите, что в вашем методе refreshVisibleViewForCell вы получаете нулевое значение для self.myTableView, Вы уверены, что он правильно подключен в IB? Даже если он подключен, нажмите на маленький крестик, чтобы отсоединить его и подключите снова, чтобы быть уверенным. Также убедитесь, что вы @synthesized ваше свойство myTableView. Не видя больше кода, проблема с IB - мое лучшее предположение относительно того, почему вы получаете нулевое значение для tableView. Нулевое значение здесь приведет к нулевым indexPath и selectedCell, а также. Что касается вашего большого вопроса, вы можете получить доступ к свойствам объектов в вашем контроллере представления. Эти свойства, конечно, могут быть объектами. Так что в вашем примере, если у вас есть свойство тега на selectedCell, вы можете получить к нему доступ из любого места, где у вас есть действительная ссылка на selectedCell. Если selectedCell равен нулю, свойство будет равно нулю. @class лучше подходит для заголовочных файлов. Например, если вы хотите сделать свою пользовательскую ячейку свойством контроллера представления, вы можете сказать:

#import <UIKit/UIKit.h>

@class MyCell;

@interface RootViewController : UIViewController

@property (nonatomic, strong) MyCell *cell;

@end

Затем в вашем файле реализации вы фактически импортируете MyCell.h. Если вы объявите форвард @class, вам не нужно будет импортировать все подробности о классе MyCell в заголовочный файл. Заголовок не должен знать обо всех свойствах и методах MyCell, просто вы намереваетесь использовать его в файле реализации. Таким образом, вы @class в заголовке, #import в реализации.

0 голосов
/ 03 апреля 2012

в RootViewController.h:

@interface RootViewController : UITableViewController <UITableViewDelegate>

в RootViewController.m:

- (void) refreshVisibleViewForCellTagged:(NSInteger)cellTag {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
MyCell *selectedCell = (MyCell*)[self.myTableView cellForRowAtIndexPath:indexPath]; // nil

и т.д ...

0 голосов
/ 03 апреля 2012

Я не вижу деклараций myTableView в вашем RootViewController. Но если ваш RootViewController реализует UITableViewController, вы можете использовать self.tableView для доступа к представлению таблицы. Вам не нужно хранить ссылку на него в одиночку.

@ RachelD, если ваш RootView сложнее, чем просто UITableViewController, подумайте об использовании отдельного класса, такого как RootTableViewController. Затем в вашей RootView xib создайте IBOutlet для RootTableViewController для ссылки на него. Как это:

// RootTableViewController definition
@interface RootTableViewController : UITableViewController
{
}

// RootViewController definition
@interface RootViewController : UIViewController
{
    RootTableViewController *table_c;
}

@property (nonatomic, retain) IBOutlet RootTableViewController *table_c;

Обратите внимание, что вам нужно перетащить «Объект» в раздел «Объекты» (для RootViewController) в построителе интерфейса и ввести RootTableViewController в разделе Пользовательский класс для этого объекта. Щелкните правой кнопкой мыши по этому объекту, убедитесь, что его IBOutlet, view, 2 делегата установлены правильно.

Причина, по которой ваш myTableView равен нулю, заключается в том, что он неправильно инициализирован. Я имею в виду, что если вы не используете UITableViewController, вы несете ответственность за его ручное назначение через конструктор интерфейса или что-то в этом роде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...