为什么多线程不在这个实现中工作?

时间:2012-08-22 14:58:26

标签: objective-c ios multithreading

Q1:我可以调用一个方法并让它在后台线程上从当前在主线程上执行的另一个方法执行吗?

Q2:作为上述内容的扩展,我可以调用一个方法,让它从另一个当前在其他后台线程本身上执行的方法中的后台线程上执行吗?

Q3:最后一个问题给出了上述问题:如果我在某个线程(主/背景)上初始化某个对象X的实例然后有一个方法Y,那个对象X,在其他一些后台线程上执行,可以这个方法Y发送消息并更新int property(例如该对象X,或者这种通信是不可能的?

我问这最后一个问题的原因是因为我一遍又一遍地重复,我无法弄清楚这里有什么问题:

以下代码返回零加速度和零度值:

MotionHandler.m

@implementation MotionHandler

@synthesize currentAccelerationOnYaxis; // this is a double

-(void)startCompassUpdates
{
    locationManager=[[CLLocationManager alloc] init];
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    locationManager.delegate=self;
    [locationManager startUpdatingHeading];
    NSLog(@"compass updates initialized");
}

-(int) currentDegrees
{
    return (int)locationManager.heading.magneticHeading;
}
-(void) startAccelerationUpdates
{
    CMMotionManager *motionManager = [[CMMotionManager alloc] init];
    motionManager.deviceMotionUpdateInterval = 0.01;
    [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
                                       withHandler:^(CMDeviceMotion *motion, NSError *error)
                                       {
                                           self.currentAccelerationOnYaxis = motion.userAcceleration.y;
                                       }
    ];
}

@end

Tester.m

@implementation Tester

-(void)test
{
    MotionHandler *currentMotionHandler = [[MotionHandler alloc] init];

    [currentMotionHandler performSelectorInBackground:@selector(startCompassUpdates) withObject:nil];

    [currentMotionHandler performSelectorInBackground:@selector(startAccelerationUpdates) withObject:nil];

    while(1==1)
    {
        NSLog(@"current acceleration is %f", currentMotionHandler.currentAccelerationOnYaxis);
        NSLog(@"current degrees are %i", [currentMotionHandler currentDegrees]);
    }

SomeViewController.m

@implementation SomeViewController
-(void) viewDidLoad
{
    [myTester performSelectorInBackground:@selector(test) withObject:nil];
}
@end

但是,以下代码通常会返回这些值:

Tester.m

@interface Tester()
{
    CLLocationManager *locationManager;
    double accelerationOnYaxis;
    // more code..
}
@end

@implementation Tester

- (id) init
{
    locationManager=[[CLLocationManager alloc] init];
    locationManager.desiredAccuracy = kCLLocationAccuracyBest;
    locationManager.delegate=self;
    [locationManager startUpdatingHeading];

    // more code..
}


-(void) test
{
    CMMotionManager *motionManager = [[CMMotionManager alloc] init];
    motionManager.deviceMotionUpdateInterval = 0.01;
    [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
                                       withHandler:^(CMDeviceMotion *motion, NSError *error)
     {
         accelerationOnYaxis = motion.userAcceleration.y;
     }
     ];

     while(1==1)
    {
        NSLog(@"current acceleration is %f", accelerationOnYaxis);
        NSLog(@"current degrees are %i", locationManager.heading.magneticHeading);
    }
}

SomeViewController.m

@implementation SomeViewController

-(void) viewDidLoad
{
    [myTester performSelectorInBackground:@selector(test) withObject:nil];
}

第一个版本出了什么问题?我真的很想使用第一个,因为它在设计方面似乎更好..感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

调用performSelectorInBackground:withObject:与使用当前对象,选择器和参数对象作为参数(Threading Programming Guide)调用detachNewThreadSelector:toTarget:withObject: NSThread方法相同。无论你在哪里调用它,都会创建一个新线程来执行该选择器。所以回答你的前两个问题:是和是。

对于最后一个问题,只要此Object X在两个方法中都是同一个对象,就可以更新X的任何属性。但是,要注意这会产生意想不到的结果(即见Concurrency Programming Guide)。如果多个方法正在更新X的属性,则可以覆盖或忽略值。但是,如果您只是从方法Y更新它并从所有其他方法中读取它,则不应发生此类问题。

答案 1 :(得分:1)

你应该看一下Grand Central Dispatch documentation from Apple。它允许您在基于块的结构中使用多个线程。

2个重要功能是dispatch_sync()dispatch_async()

一些例子:

在后台线程上执行某个代码块并等待它完成:

__block id someVariable = nil;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    // do some heavy work in the background

    someVariable = [[NSObject alloc] init];
});

NSLog(@"Variable: %@", someVariable);

此函数修改稍后可以使用的变量some​​Variable。请注意,主线程将暂停以等待后台线程。如果这不是您想要的,您可以使用dispatch_async(),如下所示:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    // do some heavy work in the background

    NSObject *someVariable = [[NSObject alloc] init];

    // notify main thread that the work is done
    dispatch_async(dispatch_get_main_queue(), ^{
        // call some function and pass someVariable to it, it will be called on the main thread
        NSLog(@"Variable: %@", someVariable);
    });
});