我想要做的是实现键值观察模式。
我有MasterViewController,DetailViewController和名为Animal的随机类,它只有一个名为“name”的属性。(顺便说一下,这个项目是从 Master-View控制器模板构建的)
// Animal.h
#import <Foundation/Foundation.h>
@interface Animal : NSObject
@property NSString *name;
@end
在detailViewController中只有一个标签使用segue显示来自动物类的name属性。并且,我也将Observer添加到动物Obj。
//detailViewController.h
#import <UIKit/UIKit.h>
@interface DetailViewController : UIViewController
@property (strong, nonatomic) id detailItem;
@property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
@end
//implementation
@implementation DetailViewController
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
[newDetailItem addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
// Update the view.
[self configureView];
}
}
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self configureView];
}
- (void)configureView
{
// Update the user interface for the detail item.
if (self.detailItem) {
self.detailDescriptionLabel.text = [self.detailItem name];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
在MasterViewController中,这是一个tableViewController,每当按下“+”栏按钮时,它会将单元格的细节设置为Animal类中的名称。而且,此时在这个方法中(insertNewObject :)我正在通过让它进入睡眠来模拟下载某些东西(5); 然后,它会将随机数分配给来自animal的name属性。 当你更改名称属性时,因为我已经将Observer添加到Animal,它会通知我并“更新”我的标签! SO,Sinarrio就是按下“+”按钮后,单击刚刚插入的单元格并等待原始标签更改。
- (void)insertNewObject:(id)sender
{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
Animal *animal = [[Animal alloc]init];
animal.name = [NSString stringWithFormat:@"animal number %d", _objects.count+1 ];
[_objects insertObject: animal atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(5);// simulating downloading.
NSLog(@"3");
NSLog(@"4");
// assign random number.
animal.name = [NSString stringWithFormat:@"animal number %d", rand()];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"7");
[self.tableView reloadData]; // **it got called after excuting NSLog(@"8") why..?**
NSLog(@"8");
});
NSLog(@"5");
NSLog(@"6");
});
NSLog(@"2");
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
NSLog(@"1");
Animal *animal = _objects[indexPath.row];
cell.textLabel.text = [animal name];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Animal *animal = _objects[indexPath.row];
[[segue destinationViewController] setDetailItem:animal];
}
}
所以,这就是我的有线行为。 至于调度线程的oder,我打印了数字,但有些时候它有不同的顺序。 但是,当我得到这个订单时,我真的不明白: 1 2 3 4 7 5 6 8 1 我认为在单线程执行中,它不应该跳转,特别是7 8 1订单部分..
另外一个问题是,在3和4之后,因为它改变了name属性,我的configure方法立刻被调用但是它没有更新或刷新任何东西,直到..实际上在一段时间之后5 6 7 8印刷
最后, 按“+”按钮后,从细节到主视图来回然后 你有这个错误和为什么.. ?
PS:当KVO在name属性更改后调用My configure方法时,当我调试时,name属性已更改但它没有更新..
答案 0 :(得分:0)
首先,对于您的第一个问题,您需要了解调度员的工作方式。
使用 global_queue 中的dispatch_async
功能(即并发)执行主题的顺序可以每次更改。不是不变的。要更好地理解,请阅读我在stackoverflow上写的文章:
How does the dispatcher work when mixing sync/async with serial/concurrent queue?
另一件事是您将@property NSString *name
声明为atomic
。实际上,当您不写任何内容时,atomic
是默认值。我不知道你是否自愿提出这个问题,但无论如何,我会写:
@property (copy, nonatomic) NSString *name;
尝试重新运行代码并阅读我的文章。现在我会更好地阅读你的帖子,我可以在这个回复中写下其他内容。
答案 1 :(得分:0)
看起来animal.name是不可变的。多项任务伤害。