I have a method that takes a block as an argument. That block needs to be augmented and then passed to a library function that block as an argument. An example:
typedef void (^eblock_t)(void);
void libraryFunction(eblock_t block);
- (void)myMethod:(eblock_t)block {
libraryFunction ( ^{
block();
NSLog(#"block executed"); // This is the augmentation of the block
} );
}
That example is pretty straight forward and works for straight forward situations. I evolved that example a bit to the following using GHUnit. It is a bit contrived, but works to illustrate my problem as concisely as possible:
EBlock.h
typedef void (^eblock_t)(void);
#interface EBlock : NSObject {
eblock_t _block;
}
#property (nonatomic, readwrite, strong) eblock_t blockOption1;
#property (nonatomic, readwrite, strong) eblock_t blockOption2;
- (void)chooseBlock:(NSUInteger)option;
- (void)executeBlock;
#end
EBlock.m
#import "EBlock.h"
#implementation EBlock
- (void)chooseBlock:(NSUInteger)option {
if (1 == option) {
// This is a block wrapping a block to augment the block
// This is the source of problem with test_switchOption_1For2
_block = ^{
self.blockOption1();
NSLog(#"option1"); // This is the augmentation
};
} else {
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
}
- (void)executeBlock { _block(); }
#end
Test_EBlock.h
#class EBlock;
#interface Test_EBlock : GHTestCase
#property (nonatomic, readonly) NSUInteger counter1;
#property (nonatomic, readonly) NSUInteger counter2;
- (void)incrementCounter1;
- (void)incrementCounter2;
#end
Test_EBlock.m
#import "Test_EBlock.h"
#import "EBlock.h"
#implementation Test_EBlock
- (void)incrementCounter1 { _counter1++; }
- (void)incrementCounter2 { _counter2++; }
- (void)setUp {
[super setUp];
_counter1 = _counter2 = 0u;
}
- (void)tearDown { [super tearDown]; }
- (void)test_option1 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:1];
[foo executeBlock];
GHAssertEquals(self.counter1, 1u, nil);
GHAssertEquals(self.counter2, 0u, nil);
}
- (void)test_option2 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:2];
[foo executeBlock];
GHAssertEquals(self.counter1, 0u, nil);
GHAssertEquals(self.counter2, 1u, nil);
}
- (void)test_switchOption_1For2 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:1];
// switch what is done in the block
foo.blockOption1 = ^{ [self incrementCounter2]; };
[foo executeBlock];
GHAssertEquals(self.counter1, 1u, nil); // This fails
GHAssertEquals(self.counter2, 0u, nil); // This fails
}
- (void)test_switchOption_2For1 {
EBlock *foo = [[EBlock alloc] init];
foo.blockOption1 = ^{ [self incrementCounter1]; };
foo.blockOption2 = ^{ [self incrementCounter2]; };
[foo chooseBlock:2];
// switch what is done in the block
foo.blockOption2 = ^{ [self incrementCounter1]; };
[foo executeBlock];
GHAssertEquals(self.counter1, 0u, nil);
GHAssertEquals(self.counter2, 1u, nil);
}
Discussion
Test: test_option1, test_option2, & test_switchOption_2For1 pass.
test_switchOption_1For2 fails because of GHAssertEquals(self.counter1, 0u, nil); and GHAssertEquals(self.counter2, 1u, nil);
This is because the block that is being executed self.blockOption1 is actually [self incrementCounter2] and not [self incrementCounter1]. This is because in EBlock.m chooseBlock the block wrapping the block has copied self.blockOption1 which at the time of evaluation is [self incrementCounter2]. Is there a better way to augment the block so the block does not have to be wrapped? Or is there a way not to delay the evaluation of self.blockOption1 so that it is [self incrementCounter1].
What is captured by your wrapping block is self, not the value of self.blockOption1. If you want to capture the latter, try:
- (void)chooseBlock:(NSUInteger)option {
if (1 == option) {
eblock_t local_block = self.blockOption1;
// This is a block wrapping a block to augment the block
_block = ^{
local_block();
NSLog(#"option1"); // This is the augmentation
};
} else {
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
}
If I understand you correctly you wish to delay the effect of chooseBlock until executeBlock to that the block can be changed between them. Just rearrange your logic (typed directly into SO, could be tided up):
#import "EBlock.h"
#implementation EBlock
{
NSUInteger currentChoice;
}
- (void)chooseBlock:(NSUInteger)option
{
currentChoice = option;
}
- (void)executeBlock
{
if (1 == option)
{ // This is a block wrapping a block to augment the block
// This is the source of problem with test_switchOption_1For2
_block = ^{
self.blockOption1();
NSLog(#"option1"); // This is the augmentation
};
}
else
{
// There is no block wrapping the block and thus no augmentation of the block
// There is no issue with test_switchOption_2For1
_block = self.blockOption2;
}
_block();
}
#end
You may want to copy the block that came in as a argument/property. Try go with:
#property (copy) eblock_t block;
Related
My app crashed, suspected to be caused by multi-threaded operation of an attribute in a singleton object.
So I wrote a small piece of code and successfully reproduced the problem, but I still couldn't understand it.
I have defined the property as #property, which is atomic. Why does it still crash when accessed by multiple threads? Below is my code snippet:
Audio.h
#interface Audio : NSObject
#property NSString *audioName;
#property NSString *audioData;
#end
Audio.m
#import "Audio.h"
#implementation Audio
- (instancetype)init{
self = [super init];
if (self) {
_audioData = #"";
_audioName = nil;
}
return self;
}
#end
AudioManager.h
#interface AudioManager : NSObject
+(instancetype)shareInstance;
#property Audio *curAudio;
-(void) play;
-(void) clearCurAudio;
#end
AudioManager.m
#import "AudioManager.h"
#implementation AudioManager
static id sharedInstance = nil;
+(instancetype)shareInstance {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void) play {
NSLog(#"Current Audio name : %#",_curAudio.audioName);
NSLog(#"Current Audio name : %#",_curAudio.audioData);
NSLog(#"Current Audio name : %#",_curAudio.audioName);//crahed here!
NSLog(#"Current Audio name : %#",_curAudio.audioData);
}
-(void) clearCurAudio {
_curAudio = nil;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
dispatch_queue_t thread1 = dispatch_queue_create("queue1", nil);
dispatch_queue_t thread2 = dispatch_queue_create("queue2", nil);
dispatch_queue_t thread3 = dispatch_queue_create("queue3", nil);
dispatch_async(thread1, ^{
for (int i = 0; i < 1000; i++) {
Audio *newAudio = [[Audio alloc] init];
newAudio.audioName = #"na";
[[AudioManager shareInstance] setCurAudio:newAudio];
}
});
//
dispatch_async(thread2, ^{
for (int i = 0; i < 1000; i++) {
[[AudioManager shareInstance] play];
}
});
//
dispatch_async(thread3, ^{
for (int i = 0; i < 1000; i++) {
AudioManager * audioManager = [AudioManager shareInstance];
[[AudioManager shareInstance] clearCurAudio];
}
});
}
Here is the crash EXC_BAD_ACCESS:
enter image description here
Thank you guys,problem solved!!, as #Willeke posted.
use self.curAudio rather than _curAudio.
I have a IKImageBrowserView which has its own controller - BrowserController.h + .m
#interface BrowserController : NSWindowController{
NSMutableArray *_images;
}
#property (strong,nonatomic) IBOutlet IKImageBrowserView *imageBrowser;
-(void)addMultipleImages;
When I run the app for the first time, the staring image loads, but when I click a button to add other images and call a method from another class I don't get any results. I have noticed that my _imageBrowser loses the reference and becomes nil.
How could I solve this issue?
AppDelegate.m
#import "AppDelegate.h"
#import "BrowserController.h"
#implementation AppDelegate{
BrowserController *imageBrowserController;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
imageBrowserController = [BrowserController sharedManager];
}
- (IBAction)doSomething:(id)sender {
[imageBrowserController addMultipleImages];
}
#end
BrowserController.m
#import "BrowserController.h"
#interface myImageObject : NSObject
{
NSString *_path;
}
#end
#implementation myImageObject
/* our datasource object is just a filepath representation */
- (void)setPath:(NSString *)path
{
if(_path != path)
{
_path = path;
}
}
/* required methods of the IKImageBrowserItem protocol */
#pragma mark -
#pragma mark item data source protocol
/* let the image browser knows we use a path representation */
- (NSString *)imageRepresentationType
{
return IKImageBrowserPathRepresentationType;
}
/* give our representation to the image browser */
- (id)imageRepresentation
{
return _path;
}
/* use the absolute filepath as identifier */
- (NSString *)imageUID
{
return _path;
}
#end
#interface BrowserController ()
#end
#implementation BrowserController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)awakeFromNib{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
- (void)updateDataSource{
[_imageBrowser reloadData];
}
-(NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser{
return [_images count];
};
-(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index{
return [_images objectAtIndex:index];
};
- (void)updateDatasource
{
[_imageBrowser reloadData];
}
- (void)addMultipleImages{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[_images addObject:p];
[_images addObject:p];
[_imageBrowser reloadData];
}
+ (id)sharedManager {
static BrowserController *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
#end
There is some confusion as to the name of the class where you mention it's called ImageBrowserController at the start of your question and it looks like it's called BrowserController at the end of your question.
The issue is that you allocate _images in awakeFromNib which is never called given the class is created via the singleton pattern (sharedInstance) and not loaded from a .nib file.
Therefore move the code from awakeFromNib into init and dump awakeFromNib:
BrowserController.m:
- (id)init
{
self = [super init];
if (self) {
myImageObject *p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
return self;
}
Further confusion: you have implemented an initWithFrame method. Why?
I have an NSObject that I create inside a block. As per the code below:
__block NSObject *myObject;
[self myMethod:^{
myObject = [[NSObject alloc] init];
....
}];
if(myObject == nil){
NSLog(#"Why is my object nil?!");
}
In the definition of myMethod I have the following:
backgroundQueue = dispatch_queue_create("backgroundqueue", NULL);
dispatch_async(backgroundQueue,
^{
dispatch_async(dispatch_get_main_queue(),
^{
if(block){
block();//Never called.
}
});
However the block is never called.
The problem here is that you never seem to execute the block in which you instantiate myObject. For illustration, run this little program:
#import <Foundation/Foundation.h>
typedef void(^MyTestBlock)(void);
#interface Foo:NSObject
- (id)initWithBlock:(MyTestBlock)aBlock;
- (void)someMethod;
#end
#implementation Foo {
MyTestBlock _block;
}
- (id)initWithBlock:(MyTestBlock)aBlock {
self = [super init];
if( !self ) { return nil; }
_block = aBlock;
return self;
}
- (void)someMethod {
_block();
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
__block NSObject *myObject;
Foo *myFoo = [[Foo alloc] initWithBlock:^{
myObject = [[NSObject alloc] init];
}];
[myFoo someMethod];
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
}
}
This prints 2012-11-26 05:00:58.519 Untitled 2[23467:707] Your object was created to the console. The point is that blocks don't execute themselves. In the code above, although we set the block as an ivar of the class, we don't execute it until we call someMethod on our Foo.
EDIT:
An edit to your question states that the block is not executed in the context of an asynchronous dispatch block sent to the main queue. If this is a command line application, then you must call dispatch_main() at the end of main. See the man page for dispatch_get_main_queue(). Here is a full working command line application to illustrate this, as well as issues related to race conditions:
#import <Foundation/Foundation.h>
typedef void(^MyTestBlock)(void);
#interface Foo:NSObject
- (id)initWithBlock:(MyTestBlock)aBlock;
- (void)someMethod;
#end
#implementation Foo {
MyTestBlock _block;
}
- (id)initWithBlock:(MyTestBlock)aBlock {
self = [super init];
if( !self ) { return nil; }
_block = aBlock;
return self;
}
- (void)someMethod {
dispatch_queue_t backgroundQueue = dispatch_queue_create("backgroundqueue", NULL);
dispatch_async(backgroundQueue, ^{
dispatch_queue_t innerQueue = dispatch_get_main_queue();
dispatch_async(innerQueue, ^{
if( _block){
NSLog(#"Will call block.");
_block();
}
});
});
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
__block NSObject *myObject;
Foo *myFoo = [[Foo alloc] initWithBlock:^{
myObject = [[NSObject alloc] init];
}];
[myFoo someMethod];
// this log statement should show that myObject is nil because it will (probably)
// be executed before your block.
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
// wait a little bit to resolve race condition (just for illustrative purposes)
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.4f * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog((myObject)?#"Your object was created":#"Why is my object nil?");
});
}
// this isn't a Cocoa app, so must call dispatch_main() at end of main
dispatch_main();
}
You have forgotten to call your block in your myMethod. Try the code bellow.
typedef void(^MyBlock)();
- (void)myMethod:(MyBlock)aBlock
{
aBlock();
}
I am a newbie to Objective-C. I'm currently working on threads.
I have to make a synchronous execution of threads. I'm using NSInvocationOperaion to spawn a thread.
I have two threads. I need to wait for the 1st thread to signal a event or the timeout.
Signalling a event can be done by NSConditionLock. How to signal a timeout. I could not use waitUntilDate method here as the timeout is not a fixed value.
Is there any way to do this?
EDITED
main.m
------
#import "PseudoSerialQueue.h"
#import "PseudoTask.h"
int main()
{
PseudoSerialQueue* q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test2)];
[q quit];
return 0;
}
PseudoTask.h
-----------------
#import <Foundation/Foundation.h>
#interface PseudoTask : NSObject {
id target_;
SEL selector_;
id queue_;
}
#property(nonatomic,readonly)id target;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
-(void)exec;
#end
PseudoTask.m
-----------------
#import "PseudoTask.h"
#implementation PseudoTask
#synthesize target = target_;
-(id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue
{
self = [super init];
if (self) {
target_ = [target retain];
selector_ = selector;
queue_ = [queue retain];
}
return self;
}
-(void)exec
{
[target_ performSelector:selector_];
}
-(void)dealloc
{
[super dealloc];
[target_ release];
[queue_ release];
}
#end
PseudoSerialQueue.h
----------------------------
#import <Foundation/Foundation.h>
#import "PseudoTask.h"
#interface PseudoSerialQueue : NSObject {
NSCondition* condition_;
NSMutableArray* array_;
NSThread* thread_;
}
-(void)addTask:(id)target selector:(SEL)selector;
#end
PseudoSerialQueue.m
----------------------------
#import "PseudoSerialQueue.h"
#implementation PseudoSerialQueue
-(id)init
{
self = [super init];
if (self) {
array_ = [[NSMutableArray alloc]init];
condition_ = [[NSCondition alloc]init];
thread_ = [[NSThread alloc] initWithTarget:self selector:#selector(execQueue) object:nil];
[thread_ start];
}
return self;
}
-(void)addTask:(id)target selector:(SEL)selector
{
[condition_ lock];
PseudoTask* task = [[PseudoTask alloc] initWithTarget:target selector:selector queue:self];
[array_ addObject:task];
[condition_ signal];
[condition_ unlock];
}
-(void)quit
{
[self addTask:nil selector:nil];
}
-(void)execQueue
{
for(;;)
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
[condition_ lock];
if (array_.count == 0) {
[condition_ wait];
}
PseudoTask* task = [array_ objectAtIndex:0];
[array_ removeObjectAtIndex:0];
[condition_ unlock];
if (!task.target) {
[pool drain];
break;
}
[task exec];
[task release];
[pool drain];
}
}
-(void)dealloc
{
[array_ release];
[condition_ release];
[super dealloc];
}
#end
I could not pass self from main.Hope i'm mistakenly calling it.
Error:'self' undeclared is coming.
I could not understand
-(void)exec
{
[target_ performSelector:selector_];
}
in PseudoTask.m
target_ is not a method and its an ivar.
I am not getting any error or warning.But i could not understand that code.
I am writing what i have understood from your program.Please correct me if i my way of understanding the program is wrong.
The Thread execQueue is spawned when the PseudoSerialQueue is initialised and it waits for the signal from the addTask method.
The addTask method is called in the quit method and the parameters passed are nil.I could not understand why to pass a nil parameter.
It would be helpful if you explain about it.Thanks.
You mean NSCondition? You can use waitUntilDate: as relative time.
[condition lock];
// wait 5 seconds.
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
[condition unlock];
EDITED:
My PseudoSerialQueue class requires to be called from a class that is derived from NSObject like the following.
#interface Test : NSObject
#end
#implementation Test
- (void)test0
{
}
- (void)test1
{
}
- (id)init
{
self = [super init];
return self;
}
- (void)exec
{
PseudoSerialQueue *q = [[PseudoSerialQueue alloc] init];
[q addTask:self selector:#selector(test0)];
[q addTask:self selector:#selector(test1)];
[q addTask:self selector:#selector(test0)];
[q quit];
}
#end
You can call it from main function.
Test *test = [[Test alloc] init];
[test exec];
I could not understand why to pass a nil parameter.
I just only chose it for the message of quitting the loop in the PseudoSerialQueue.
Let the 1st thread signal the 2nd one in both cases; then in the second thread you can tell in which case you are based on some read-only flag in the 1st controller or in your model (say, isDataAvailable).
I have a a singleton class here is the code
#import <Foundation/Foundation.h>
#import "CustomColor.h"
#interface Properties : NSObject {
UIColor *bgColor;
CustomColor *bggColor;
}
#property(retain) UIColor *bgColor;
#property (retain) CustomColor *bggColor;
+ (id)sharedProperties;
#end
#import "Properties.h"
static Properties *sharedMyProperties = nil;
#implementation Properties
#synthesize bgColor;
#synthesize bggColor;
#pragma mark Singleton Methods
+ (id)sharedProperties
{
#synchronized(self)
{
if(sharedMyProperties == nil)
[[self alloc] init];
}
return sharedMyProperties;
}
+ (id)allocWithZone:(NSZone *)zone
{
#synchronized(self)
{
if(sharedMyProperties == nil)
{
sharedMyProperties = [super allocWithZone:zone];
return sharedMyProperties;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release {
// never release
}
- (id)autorelease {
return self;
}
- (id)init {
if (self = [super init])
{
bgColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1.0];
FSColor *bc = [[FSColor alloc] init];
bc.red = bc.green = bc.blue = bc.hue = bc.sat = bc.bri = 0;
bggColor = bc;
}
return self;
}
- (void)dealloc
{
// Should never be called, but just here for clarity really.
[bgColor release];
[bggColor release];
[super dealloc];
}
#end
I have a UIView' subclass. in which i am using it. I am calling drawRect method after each second. It only run once and then app crashes.
- (void)drawRect:(CGRect)rect
{
Properties *sharedProprties = [Properties sharedProperties];
…...
….
CGContextSetFillColorWithColor(context, [[sharedProprties bgColor] CGColor]);
…..
}
What if you do self.bgColor = [UIColor colorWithRed:...]?
Without the self. I think you may be accessing the ivar directly, and therefore assigning it an autoreleased object which won't live very long (rather than using the synthesized property setter, which would retain it). I could be wrong about this, I'm stuck targeting older Mac OS X systems so I haven't been able to play much with Objective-C 2.0.
Your static sharedProperties gets never assigned. It is missing in the init method or in the sharedProperties static method.
Here's a sample pattern for singletons (last post)
Also accessing properties without self. may cause bad access errors too.
Regards
Not an answer, but note that a simple call to [Properties alloc] will mean it's no longer a singleton!
+ (id)sharedProperties
{
#synchronized(self)
{
if(sharedMyProperties == nil)
{
sharedMyProperties = [[self alloc] init];
}
}
return sharedMyProperties;
}