ios – Objective-C中原子/非原子的证据

前端之家收集整理的这篇文章主要介绍了ios – Objective-C中原子/非原子的证据前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在阅读 Apple’s documentation之后,我尝试在Objective-C中提供属性的原子性或非原子性.为此,我创建了一个具有名和姓的Person.

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的一个实例.

@property (strong,nonatomic) Person *p;

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

根据我的理解,我可以在我的日志中输出Bob Frost或Jack Sponge,因为我声明我的属性是非原子的.但那并没有发生.我不明白为什么.我错过了什么或误解了什么吗?

- (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;
}

解决方法

具有非原子属性使得部分写入的可能性成为可能,但决不是确定的.

在Person类中,设置名字和姓氏的唯一方法是在init方法中,然后设置第一个名称,然后设置紧随其后的姓氏.设置名字和姓氏将非常接近彼此,很少有机会让另一个线程在操作之间混乱.

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

也就是说,你的代码无法预测的是什么人物在任何时刻都会在self.p中出现.您应该看到Bob Sponge和Jack Frost之间显示的值无法预测.

一个更好的测试将是这样的:

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

@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",_thing2.x1,_thing2.x2);
 }

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

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

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

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

(注2,编辑我的代码的人:代码格式是风格和个人品味的问题.我使用“Allman缩进”的变体.我欣赏错别字修正,但我鄙视K& R风格的缩进.Don’把你的风格强加给我的代码.

原文链接:https://www.f2er.com/iOS/334621.html

猜你在找的iOS相关文章