看完Apple's documentation ,我尝试在 Objective-C 中证明属性的原子性或非原子性。为此,我创建了一个具有名字和姓氏的 Person 类。
人.h
@interface Person : NSObject
@property (nonatomic, strong) NSString *firstName;
@property (nonatomic, strong) NSString *lastName;
- (instancetype)initWithFirstName:(NSString *)fn lastName:(NSString *)ln;
@end
人.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,因为我将我的属性声明为 nonatomic。但那并没有发生。我不明白为什么。我是否遗漏了什么或误解了什么?
- (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 对象。当您当前的代码运行时,对象已经存在并且您不再更改它们的名称值,因此不会出现竞争条件或使用名称值进行部分写入。您只是在两个对象之间更改 self.p,这两个对象一旦创建就不会更改。
也就是说,您的代码不可预测的是 person 对象随时会在 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",
equalString,
_thing2.x1,
_thing2.x2);
}
在上面的代码中,每个队列创建一系列随机值,并在重复循环中将几个对象的 x1 和 x2 属性设置为这些随机值。它在设置每个对象的 x1 和 x2 属性之间延迟一个小的随机间隔。该延迟模拟后台任务需要花费一些时间来完成本应是原子的工作。它还引入了一个窗口,在该窗口中,另一个线程可以在当前线程能够设置第二个值之前更改第二个值。
如果您运行上面的代码,您几乎肯定会发现 thing1 和 thing2 的 x1 和 x2 值有时不同。
上面的代码不会得到原子属性的帮助。您需要在设置每个对象的 x1 和 x2 属性之间断言某种锁(可能使用 @synchronized 指令)。
(请注意,我在论坛编辑器中将上面的代码拼在一起。我没有尝试编译它,更不用说调试它了。毫无疑问,其中有一些错别字。)
(注意 2,对编辑我的代码的人:代码格式是风格和个人品味的问题。我使用“Allman 缩进”的变体。我感谢拼写错误更正,但我鄙视 K&R 风格的缩进。不要'将您的风格强加于我的代码。
关于ios - Objective-C 中原子/非原子的证据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34384807/
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我正在尝试为我的iOS应用程序设置cocoapods但是当我执行命令时:sudogemupdate--system我收到错误消息:当前已安装最新版本。中止。当我进入cocoapods的下一步时:sudogeminstallcocoapods我在MacOS10.8.5上遇到错误:ERROR:Errorinstallingcocoapods:cocoapods-trunkrequiresRubyversion>=2.0.0.我在MacOS10.9.4上尝试了同样的操作,但出现错误:ERROR:Couldnotfindavalidgem'cocoapods'(>=0),hereiswhy:U
print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上
术语中文解释Ability原子化服务帮助用户完成任务的原子化服务,和用户的意图进行关联。Fulfillment服务履行通过图标,卡片,语音等形式呈现用户意图。开发者通过接口的方式,处理用户意图,返回内容。Intent意图用于表达用户想要达成的目标或完成的任务。HUAWEIAssistant智能助手“无微不智”的个人助手,通过不断的学习用户的使用习惯,不断的为用户提供贴心的精准的便捷的个性化服务。AISearch全局搜索用户可快速搜索关键词,与之匹配的原子化服务则会出现在搜索结果中。SmartService智慧服务用户订阅原子化服务,在到达特定触发条件(时间、地点、事件)后,卡片推送至用户智能助
当我将IO::popen与不存在的命令一起使用时,我在屏幕上打印了一条错误消息:irb>IO.popen"fakefake"#=>#irb>(irb):1:commandnotfound:fakefake有什么方法可以捕获此错误,以便我可以在脚本中进行检查? 最佳答案 是:升级到ruby1.9。如果您在1.9中运行它,则会引发Errno::ENOENT,您将能够拯救它。(编辑)这是在1.8中的一种hackish方式:error=IO.pipe$stderr.reopenerror[1]pipe=IO.popen'qwe'#
我正在为ruby开发一个C扩展,但我需要包含来自IOBluetooth框架的header,特别是:#import#import一切都编译正常,但在运行时,扩展错误:path/to/file.rb:1:in`require_relative':dlopen(/path/to/extension.bundle,9):Symbolnotfound:_OBJC_CLASS_$_IOBluetoothDeviceInquiry(LoadError)我相当确定这与未包含在链接过程中的框架有关,但我不确定原因。任何帮助将不胜感激extconf.rb:#Loadsmkmfwhichisusedto
当我尝试使用“套接字”库中的方法“read_nonblock”时出现以下错误IO::EAGAINWaitReadable:Resourcetemporarilyunavailable-readwouldblock但是当我通过终端上的IRB尝试时它工作正常如何让它读取缓冲区? 最佳答案 IgetthefollowingerrorwhenItrytousethemethod"read_nonblock"fromthe"socket"library当缓冲区中的数据未准备好时,这是预期的行为。由于异常IO::EAGAINWaitReadab
我需要将目录中的一堆文件上传到S3。由于上传所需的90%以上的时间都花在了等待http请求完成上,所以我想以某种方式同时执行其中的几个。Fibers能帮我解决这个问题吗?它们被描述为解决此类问题的一种方法,但我想不出在http调用阻塞时我可以做任何工作的任何方法。有什么方法可以在没有线程的情况下解决这个问题? 最佳答案 我没有使用1.9中的纤程,但是1.8.6中的常规线程可以解决这个问题。尝试使用队列http://ruby-doc.org/stdlib/libdoc/thread/rdoc/classes/Queue.html查看文