Objective-C中原子/非原子的证据 [英] Evidence of atomic / nonatomic in Objective-C

查看:96
本文介绍了Objective-C中原子/非原子的证据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读 Apple的文档,我试图在Objective-C中提供属性的原子性或非原子性。为此,我创建了一个具有名和姓的Person。

After reading Apple's documentation, I try to put in evidence atomicity or non-atomicity of a property in Objective-C. To do this I create a class Person which has first and last name.

Person.h

@interface Person : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;

- (instancetype)initWithFirstName:(NSString *)fn lastName:(NSString *)ln;
@end

Person.m

@implementation Person

- (instancetype)initWithFirstName:(NSString *)fn lastName:(NSString *)ln {
    if (self = [super init]) {
        self.firstName = fn;
        self.lastName = ln;
    }
    return self;
}

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

@end

在另一个班级,这里是我的AppDelegate,我有一个非原子属性,是Person的一个实例。

In another class, here my AppDelegate, I have a nonatomic property which is an instance of Person.

@property (strong, nonatomic) Person *p;

在实现文件中,我创建了三个并发队列。在第一个队列中我读了属性,在另外两个队列中我写了不同的人物值。

In the implementation file, I create three concurrent queues. In the first queue I read the property, in two other queues I write different values of person.

根据我的理解,我可以有 Bob Frost Jack Sponge ,因为我将我的属性声明为 nonatomic 。但那并没有发生。我不明白为什么。我错过了什么或误解了什么?

From what I understand, I could have Bob Frost or Jack Sponge output in my log, since I declared my property as nonatomic. But that didn't happened. I don't understand why. Am I missing something or misunderstanding something?

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.

    Person *bob = [[Person alloc] initWithFirstName:@"Bob" lastName:@"Sponge"];
    Person *jack = [[Person alloc] initWithFirstName:@"Jack" lastName:@"Frost"];
    self.p = bob;

    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue1, ^{
        while (YES) {
            NSLog(@"%@", self.p);
        }
    });

    dispatch_async(queue2, ^{
        while (YES) {
            self.p = bob;
        }
    });

    dispatch_async(queue3, ^{
        while (YES) {
            self.p = jack;
        }
    });

    return YES;
}


推荐答案

拥有非原子属性部分写入的可能性,但绝不是确定的。

Having non-atomic properties makes the possibility of partial writes possible, but by no means certain.

在Person类中,设置名字和姓氏的唯一方法是在init方法中,然后然后立即设置名字,然后是姓氏。设置名字和姓氏将彼此非常接近,几乎没有机会让另一个线程在操作之间弄乱。

In your Person class the only way you are setting first and last names is in the init method, and then you set the first name and then the last name immediately after. Setting the first name and last name will occur VERY close to each other, with little chance for another thread to mess things up between operations.

此外,您创建Person对象在主线程中,在运行并发操作之前。当您的当前代码运行时,对象已经存在,您不再更改其名称值,因此不存在竞争条件或具有名称值的部分写入的可能性。您只是在两个对象之间更改self.p,这两个对象在创建后不会更改。

Furthermore, you create your Person objects in the main thread, before you running concurrent operations. By the time your current code runs, the objects already exist and you no longer change their name values, so there's no chance of a race condition or a partial write with name values. You are simply changing self.p between 2 objects that don't change once they are created.

这就是说,您的代码无法预测的是对象将是什么在任何时刻在self.p中。您应该看到Bob Sponge和Jack Frost之间显示的值无法预测。

That said, what IS unpredictable about your code is what person object will be in self.p at any instant. You should see the values displayed alternate between Bob Sponge and Jack Frost unpredictably.

更好的测试是这样的:

(假设每个TestObject的x1和x2值应始终为保持不变。)

(Assume each TestObject's x1 and x2 values should always be kept the same.)

@interface TestObject : NSObject
@property (nonatomic, assign) int x1;
@property (nonatomic, assign) int x2;
@end

@interface AppDelegate
@property (nonatomic, strong) TestObject *thing1;
@property (nonatomic, strong) TestObject *thing2;
@property (nonatomic, strong) NSTimer *aTimer;
@property (nonatomic, strong) NSTimer *secondTimer;
@end

然后代码如下:

#include <stdlib.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
  dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
  dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);

  self.thing1 = [[TestObject alloc] init];
  self.thing2 = [[TestObject alloc] init];

  dispatch_async(queue1, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });


  //Do the same thing on queue2
  dispatch_async(queue2, ^
  {
    for (int x = 0; x < 100; x++) 
    {
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      int thing1Val = arc4random_uniform(10000);
      int thing2Val = arc4random_uniform(10000);
      _thing1.x1 = thing1Val;
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x1 = thing2Val;
      _thing1.x2 = thing1Val; //thing1's x1 and x2 should now match
      usleep(arc4random_uniform(50000)); //sleep for 0 to 50k microseconds
      _thing2.x2 = thing2Val; //And now thing2's x1 and x2 should also both match
    }
  });

  //Log the values in thing1 and thing2 every .1 second
  self.aTimer = [NSTimer scheduledTimerWithTimeInterval:.1
    target:self
    selector:@selector(logThings:)
    userInfo:nil
    repeats:YES];

  //After 5 seconds, kill the timer.
  self.secondTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
    target:self
    selector:@selector(stopRepeatingTimer:)
    userInfo:nil
    repeats:NO];
  return YES;
}

- (void)stopRepeatingTimer:(NSTimer *)timer 
{
  [self.aTimer invalidate];
}

- (void)logThings:(NSTimer *)timer 
{
  NSString *equalString;
  if (_thing1.x1 == _thing1.x2) 
  {
    equalString = @"equal";
  }
    else 
  {
    equalString = @"not equal";
  }
  NSLog(@"%@ : thing1.x1 = %d, thing1.x2 = %d", 
    equalString, 
    _thing1.x1, 
    _thing1.x2);

  if (_thing2.x1 == _thing2.x2) 
    {
      equalString = @"equal";
    }
  else 
    {
      equalString = @"not equal";
    }
  NSLog(@"%@ : thing2.x1 = %d, thing2.x2 = %d", 
    equalString, 
    _thing2.x1, 
    _thing2.x2);
 }

在上面的代码中,每个队列都会创建一系列随机值,并设置两个对象的x1和x2属性与重复循环中的那些随机值。它会延迟设置每个对象的x1和x2属性之间的小的随机间隔。该延迟模拟后台任务需要一些时间来完成应该是原子的工作。它还引入了一个窗口,其中另一个线程可以在当前线程能够设置第二个值之前更改第二个值。

In the code above, each queue creates a series of random values, and sets both the x1 and x2 properties of a couple of objects to those random values in a repeating loop. It delays for a small random interval between setting the x1 and x2 property of each object. That delay simulates a background task taking some amount of time to finish work that should be atomic. It also introduces a window where another thread could change the second value before the current thread is able to set the second value.

如果运行上面的代码,几乎可以肯定发现thing1和thing2的x1和x2值有时会不同。

If you run the code above you will almost certainly find that the x1 and x2 values of thing1 and thing2 are sometimes different.

上面的代码对原子属性没有帮助。您需要在设置每个对象的x1和x2属性之间声明某种锁(可能使用 @synchronized 指令)。

The code above would not be helped by atomic properties. You would need to assert a lock of some sort between setting the x1 and x2 property of each object (perhaps using the @synchronized directive).

(请注意,我在论坛编辑器中将上面的代码组合在一起。我没有尝试编译它,更不用说调试它了。毫无疑问有一些错别字。)

(Note that I banged the code above together in the forum editor. I haven't tried to compile it, much less debug it. There are doubtless a few typos.)

(注2,编辑我的代码的人:代码格式是风格和个人品味的问题。我使用Allman缩进的变体。我欣赏错别字修正,但我鄙视K& R样式缩进。请勿在我的代码上强加 样式。

(Note 2, to the person who edited my code: Code formatting is a matter of style and personal taste. I use a variation on "Allman indentation." I appreciate the typos corrections, but I despise K&R style indentation. Don't impose your style on my code.

这篇关于Objective-C中原子/非原子的证据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆