2014-10-01 39 views
2

我想在對象初始化期間卸載一些繁重的工作。我添加了一個status屬性,當我完全初始化了我的對象的所有其他實例變量時,我設置了該屬性。我必須通過下面的例子中簡化了我的整個做法:問題在多線程環境中獲取正確的屬性值

這是我的課(美孚),有我的初始化:

// - 頭文件

#import <Foundation/Foundation.h> 

typedef void (^loadingCompletionBlock)(BOOL success); 

typedef NS_ENUM(NSInteger, FooStatus) { 
    FooCreated, 
    FooReady, 
    FooFailed, 
}; 


@interface Foo : NSObject 

+ (id) withCompletionBlock:(loadingCompletionBlock) block; 

@property (assign, nonatomic) FooStatus status; 

@end 

// - 實現文件

@implementation Foo 

- (FooStatus) status { 

    __block FooStatus readStatus; 

    dispatch_sync([Foo concurrentLoadingQueue], ^{ 
     readStatus = _status; 
    }); 

    return readStatus; 
} 

+ (dispatch_queue_t)concurrentLoadingQueue 
{ 
    static dispatch_queue_t sharedQueue; 
    static dispatch_once_t onceToken; 

    dispatch_once(&onceToken, ^{ 
     sharedQueue = dispatch_queue_create("test.loadingQueue", DISPATCH_QUEUE_CONCURRENT); 
    }); 

    return sharedQueue; 
} 


+ (id)withCompletionBlock:(loadingCompletionBlock) completed { 

    Foo* foo = [[Foo alloc] init]; 
    foo.status = FooCreated; 

    dispatch_async([Foo concurrentLoadingQueue], ^{ 

     for (int i=0; i<10000; i++) 
     { 
      NSLog(@"Running long tasks: %d", i); 
     } 

     foo.status = FooReady; 

     completed(YES); 

     NSLog(@"Foo status: %d (Background Thread)", foo.status); 

    }); 


    return foo; 

}; 

@end 

我調用然後在主線程中下面的代碼:

Foo* foo = [Foo withCompletionBlock:^(BOOL success) { 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      NSLog(@"Foo status: %d (Main Thread)", foo.status); 

     }); 
    }]; 

    NSLog(@"Foo status: %d (Main Thread)", foo.status); 

的的NSLog會給我那些輸出:

2014-10-01 07:09:49.606 TestConcurrentQueue[15610:60b] Foo status: 0 (Main Thread) 
2014-10-01 07:09:52.818 TestConcurrentQueue[15610:1303] Running long tasks: 1 
2014-10-01 07:09:52.818 TestConcurrentQueue[15610:1303] ...... 
2014-10-01 07:09:52.818 TestConcurrentQueue[15610:1303] Running long tasks: 9999 
2014-10-01 07:09:52.818 TestConcurrentQueue[15610:60b] Foo status: 0 (Main Thread) 
2014-10-01 07:09:52.818 TestConcurrentQueue[15610:1303] Foo status: 1 (Background Thread) 

創建的0是狀態和1準備好,爲什麼我不能讓我的主線程中正確的價值?

+1

在主線程中記錄'foo.status'的完成塊中,是否設置了'foo'?看看'foo'是否爲'nil'。 – rmaddy 2014-10-01 05:20:13

+1

如果您要從多個線程訪問屬性,您應該將其聲明爲「atomic」而非「nonatomic」。此外,派遣組可能是更好的方法https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html#//apple_ref/doc/uid/TP40008091-CH102- SW25 – Paulw11 2014-10-01 05:31:21

+0

只是爲了歡笑,聲明後備存儲變量'_status'並使其成爲'volatile BOOL',看看是否有幫助。 – borrrden 2014-10-01 06:26:00

回答

1

我相信foonil內部的完成塊,因爲foo正在創建和分配作爲原始方法調用的結果。

一個簡單的解決方案是將創建的對象作爲參數傳遞給完成塊,以及BOOL

更好的方法可能是讓您的withCompletionBlock:成爲實例方法而不是類方法。然後調用代碼會是這樣的:

Foo* foo = [[Foo alloc] init]; 
[foo populateWithCompletionBlock:^(BOOL success) { 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     NSLog(@"Foo status: %d (Main Thread)", foo.status); 
    }); 
}]; 

NSLog(@"Foo status: %d (Main Thread)", foo.status); 

這種方式完成塊內的foo會引用實際存在的對象。

init方法Foo將設置初始狀態,這就是全部。

+0

感謝您的回答。你有什麼反饋btw關於我已經定義了我的getter狀態屬性的方式?這是正確的,或者我應該考慮用atomic關鍵字聲明我的屬性。我仍然有一些不確定性。 – tiguero 2014-10-01 22:32:19

+0

您知道將從多個線程訪問的任何屬性應該是'atomic'。如果你確實知道只有一個線程可以訪問給定實例的屬性,那麼比'nonatomic'好。 – rmaddy 2014-10-01 22:37:34

+0

我看到所以它應該是好的,我已經說明了情況:謝謝澄清。 – tiguero 2014-10-01 22:45:23