As Apple encourages the usage of blocks, and i wanted to do a series of animations, with sound output in between them which is basicly like a todolist, i wanted to implement this using blocks.
unfortunatly AVAudiosplayer doesnt appear to support onCompletion blocks, in the manner UIAnimation does.
So i thought it would be cool to add that support to the AVAudioplayer.
so what ive dont is this
header
#import <AVFoundation/AVFoundation.h>
#interface AVAudioPlayer (AVAudioPlayer_blockSupport)
typedef void(^AVPlaybackCompleteBlock)(void);
#property (nonatomic, copy) AVPlaybackCompleteBlock block;
-(id)initWithContentsOfURL:(NSURL*)pathURL error:(NSError**)error onCompletion:(AVPlaybackCompleteBlock) block;
-(void)setBlock:(AVPlaybackCompleteBlock)block;
-(AVPlaybackCompleteBlock)block;
-(void) executeBlock;
#end
and the m file
#import "AVAudioPlayer+blocks.h"
#implementation AVAudioPlayer (AVAudioPlayer_blockSupport)
-(id)initWithContentsOfURL:(NSURL *)pathURL error:(NSError **)error onCompletion:(AVPlaybackCompleteBlock )block {
self = [[AVAudioPlayer alloc] initWithContentsOfURL:pathURL error:error];
self.block = block;
return self;
}
-(void)setBlock:(AVPlaybackCompleteBlock)block {
self.block = block;
}
-(AVPlaybackCompleteBlock)block {
return self.block;
}
-(void) executeBlock {
if (self.block != NULL) {
self.block();
}
}
#end
after doing this, i thought i should be able to create a new audioplayer like this:
player = [[AVAudioPlayer alloc] initWithContentsOfURL:pathURL error:&error onCompletion:block];
this appears to be working.
now in the delegate will try to execute the block attached.
if (localPlayer.block) {
[localPlayer executeBlock];
}
unfortunately when i try to run the code, it appears to be looping infinitely. I wanted to use synthesize instead, but thats not for category use...
If i dont implement that Method im stuck with '-[AVAudioPlayer setBlock:]: unrecognized selector sent to instance which makes sense, since there is no method with that name.
i found this Block references as instance vars in Objective-C so i thought i should be able to attach the additional property(my Block) to the AudioPlayer.
I figured it out, i needed to use
objc_setAssociatedObject(self, &defaultHashKey, blocked, OBJC_ASSOCIATION_COPY_NONATOMIC);
to store and access the property. maybe thats what jere meant with, i have tot ake care of the memory management myself.
-(void)setBlock:(AVPlaybackCompleteBlock)blocked {
objc_setAssociatedObject(self, &defaultHashKey, blocked, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(AVPlaybackCompleteBlock)block {
return objc_getAssociatedObject(self, &defaultHashKey) ;
}
Related
#import "ViewController.h"
#interface ViewController ()
//Declare block as property
#property (nonatomic, strong) void (^dataBlock)(BOOL success);
#end
#implementation ViewController
- (void) myMethod1:(void (^)(BOOL success))response {
//Here data block holds the reference to response block
_dataBlock = response;
}
- (void) myMethod2 {
//Check for _dataBlock and invoke it.
if (_dataBlock) {
_dataBlock(YES);
}
}
- (IBAction) buttonClick {
//Call for myMethod1 and call back block is invoked in myMethod2
[self myMethod1:^(BOOL success) {
if (success) {
NSLog(#"Im Done");
}
}];
}
#end
Above sample is my code in Objective-C
Callback Block of "myMethod1"(response) is having reference/stored to "dataBlock" Property.
then invoke "dataBlock" from "myMethod2".
since "datablock" have reference to"myMethod1" block named "response", i'll be getting call back in "myMethod1", please look at the code snippet (similar to function to pointer).
same thing i want to implement in swift. I have tried implementing this in swift using closures, but not getting it.
No, there is not, unless it's on a Jailbroken device.
Apple does not let 3rd party apps alter the core behavior of the phone.
Now you could put the phone in a Faraday cage and put antennas on the inside and outside, and disconnect them when you wanted to block calls.
Actually you sort of can, but not really programmatically from iOS. If the BLE device implements the HID profile then you can simulate a double click on the lock button which would dismiss the call. I have done that, but it is a bit of a clunky solution.
I have follow countless similar situations on here ( stack overflow ). I have applied those write ups for my situation and have come close but haven't found a solution that seems work for me. As this is something very trivial I am baffled at how it isn't working for me.
Anyways, I am trying to set a Int variable from inside the appDelegate class and the variable actually belongs, if that is the right word, to another class (mainViewController).
For the MainViewController class .h file I have
#interface MainWindowControllerViewController :... {
int relayState;
}
#property (readwrite, nonatomic) int relayState;
For the MainViewController class .m file I have
#implementation MainWindowControllerViewController
#synthesize relayState = _relayState;
-(void)quickConnect { // Call for all UI bypassing and quick kill
NSLog(#"Relay state in MainWindow = %d", relayState);
if (_relayState == 1) {
NSLog(#"TURNING ON KILL SWITCH");
self.writeData = [[NSString stringWithFormat:#"%#", #"e"] dataUsingEncoding:NSASCIIStringEncoding];
[sensor write:sensor.activePeripheral data:writeData];
} else {
NSLog(#"TURNING OFF KILL SWITCH");
self.writeData = [[NSString stringWithFormat:#"%#", #"o"] dataUsingEncoding:NSASCIIStringEncoding];
[sensor write:sensor.activePeripheral data:writeData];
}
}
For the appDelegate.h file I have
#import "MainWindowControllerViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
MainWindowControllerViewController *relayState;
}
#property (nonatomic, assign) MainWindowControllerViewController *relayState;
Then for the appDelegate.m file I have
#import "MainWindowControllerViewController.h"
#implementation AppDelegate
#synthesize relayState = _relayState;
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *) sourceApplication annotation:(id)annotation {
NSLog(#" Calling application: %#", sourceApplication);
NSLog(#" URL scheme: %#", [url scheme]);
if ([sourceApplication isEqualToString:#"URL-Scheme-Test"]) {
if ([[url scheme] isEqualToString:#"TurnOffRelay"]) {
_relayState =[[MainWindowControllerViewController alloc] init];
_relayState.relayState = 1;
NSLog(#"Relay State BEFORE = %d", _relayState.relayState);
}
return YES;
if ([[url scheme] isEqualToString:#"TurnOnRelay"]) {
_relayState =[[MainWindowControllerViewController alloc] init];
_relayState.relayState = 0;
NSLog(#"Relay State BEFORE = %d", _relayState.relayState);
}
return YES;
}
else
return NO;
}
This is pretty much it. When I do the logs I find that the relayState that I want to change the value of shows that I was able to change the value. But when I do the If statement above that checks weather the relayState is equal to 1 the variable is always 0.
I'm not the most proficient in Obj C. Some help would be appreciated! Thanks. I'll gladly answer any extra questions about code if needed.
You are kind of mixing some older style obj-c with how it usually looks currently and I think you are hiding a variable (one declaration is hiding another)
Take out:
{
int relayState;
}
From your MainWindowControllerViewController interface
Take out the #synthesize in the MainWindowControllerViewController implementation
Don't use _relayState (just use relayState)
Do the same with the MainWindowControllerViewController variable in the AppDelegate.
If that doesn't fix it, we need to see what is happening to the VC you are creating.
The problem I believe is this line
_relayState =[[MainWindowControllerViewController alloc] init];
You are initializing MainWindowControllerViewController in both the if blocks which is resetting the value to the default value (In this case 0) every time.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
MainWindowControllerViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"MainWindowControllerViewControllerIdentifier"];
vc.relayState = 1;
For the MainViewController class .h
#interface MainWindowControllerViewController :... {
int relayState; //remove this line
}
#property (nonatomic) int relayState; //use this
try this
You are making a mistake that will create endless problems for you.
You have a property named relayState and an instance variable named relayState.
The property "relayState" is backed by an instance variable named _relayState. You have another instance variable named relayState. Two different variables. Assigning to self.relayState changes one, assigning to relayState changes the other. Don't do it.
Just get rid of the "int relayState;" and don't use #synthesize and you'll be fine. At least a lot finer than you are now. You can then either use self.relayState or _relayState. At least you will always know what you are doing.
PS. Having an int relayState and a MainWindowViewController* relayState is just asking for trouble. You seem to be intentionally trying to confuse yourself.
PS. You seem to do the same dangerous game with the writeData property/variable.
I have an Objective-C layer that wraps a C++ component. The current design is meant to be robust, meaning that the user may set the instance to nil anytime (ARC) and the underlying component will clean itself up properly, and synchronously.
The problem I have is that the Obj-C instance on top passes itself to the underlying C++ layer as a __weak reference to be accessed during operation (e.g. calling delegate, changing some states, etc.) and when a user deallocs the Obj-C instance by setting it to nil, an EXC_BAD_ACCESS occurs when trying to access it from inside dealloc.
Here's some sample code solely to demonstrate the problematic scenario.
Wrapper.h
#import <Foundation/Foundation.h>
#import "Underlying.h"
#interface Wrapper : NSObject {
Underlying* _cppUnderlying;
}
- (instancetype)init;
- (void)dealloc;
#end
Wrapper+Private.h
#import "Wrapper.h"
#interface Wrapper () {
#package
NSMutableDictionary* _dict;
}
#end
Wrapper.mm
#import "Wrapper+Private.h"
#implementation Wrapper
- (instancetype)init
{
self = [super init];
_cppUnderlying = new Underlying(self);
_dict = [NSMutableDictionary dictionary];
return self;
}
- (void)dealloc
{
delete _cppUnderlying;
}
#end
Underlying.h
#class Wrapper;
class Underlying
{
public:
Underlying(Wrapper* wrapper);
~Underlying();
private:
__weak Wrapper* _wrapper;
};
Underlying.mm
#include "Underlying.h"
#import "Wrapper+Private.h"
Underlying::Underlying(Wrapper* wrapper) :
_wrapper(wrapper)
{
}
Underlying::~Underlying()
{
// ERROR OCCURS HERE.
[_wrapper->_dict setValue:#"value1" forKey:#"key1"];
}
Initially, the question was "Why the error?!" but then, I just found out about this: Weak property is set to nil in dealloc but property's ivar is not nil which includes a detailed explanation (basically, objc_loadWeak() returns nil as soon as dealloc starts).
Now, the question would be:
What kind of Obj-C design practice could I adopt to avoid this situation altogether? The C++ layer takes care of all session cleanup (if any ongoing) synchronously in its destructor. It seems like the same cannot be done in Objective-C. Should I provide a 'release' or 'close' method to asynchronously do all the cleanup before the user is ALLOWED to dealloc the instance?
Thanks!
How about inside -[Wrapper dealloc], before you do delete _cppUnderlying;, you first call something like _cppUnderlying->cleanup(_dict); where you explicitly pass your dictionary, or even _cppUnderlying->cleanup(self); where you pass the entire object, and let the Underlying take care of all the cleanup there?
You're dereferencing nil with the -> operator:
[_wrapper->_dict setValue:#"value1" forKey:#"key1"];
Either test _wrapper for nil, or change make dict into a property you can access via dot notation:
if (_wrapper) {
[_wrapper->_dict setValue:#"value1" forKey:#"key1"];
}
or
[_wrapper.dict setValue:#"value1" forKey:#"key1"];
I can not compile this code:
[verify(mockedContext) deleteObject:item1];
[verify(mockedContext) deleteObject:item2];
[verify(mockedContext) save:anything()];<--compilation error for conversion id to NSError**
However I'm able to pass compilation in similar case with given macros with additional syntax:
[[given([mockedContext save:nil]) withMatcher:anything()] willReturn:nil];
Are there anything to help me pass compilation with verify?
Here is compilation error:
Implicit conversion of an Objective-C pointer to 'NSError *__autoreleasing *' is disallowed with ARC
I assume the save: method on the 'mockedContext' takes a pointer-to-pointer to NSError.
So actually, the NSError must be seen as an extra return value of the save:method. This means that you should rather setup an expectation in the first place.
I worked out a small example:
We start with the Context protocol with a simple method taking an NSError**.
#protocol Context <NSObject>
- (id)doWithError:(NSError *__autoreleasing *)err;
#end
Next is a class using this protocol, much like your SUT. I called it ContextUsingClass
#interface ContextUsingClass : NSObject
#property (nonatomic, strong) id<Context> context;
#property BOOL recordedError;
- (void)call;
#end
#implementation ContextUsingClass
- (void)call {
NSError *error;
[self.context doWithError:&error];
if (error) {
self.recordedError = YES;
}
}
#end
As you can see, when the context method doWithError: returns an error, the recordedError property is set to YES. This is something we can expect to be true or false in our test. The only problem is, how do we tell the mock to result in an error (or to succeed without error)?
The answer is fairly straight forward, and was almost part of your question: we pass an OCHamcrest matcher to the given statement, which in turn will set the error for us through a block. Bear with me, we'll get there. Let's first write the fitting matcher:
typedef void(^ErrorSettingBlock)(NSError **item);
#interface ErrorSettingBlockMatcher : HCBaseMatcher
#property (nonatomic, strong) ErrorSettingBlock errorSettingBlock;
#end
#implementation ErrorSettingBlockMatcher
- (BOOL)matches:(id)item {
if (self.errorSettingBlock) {
self.errorSettingBlock((NSError * __autoreleasing *)[item pointerValue]);
}
return YES;
}
#end
This matcher will call the errorSettingBlock if it has been set, and will always return YES as it accepts all items. The matchers sole purpose is to set the error, when the test asks as much. From OCMockito issue 22 and it's fix, we learn that pointer-to-pointers are wrapped in NSValue objects, so we should unwrap it, and cast it to our well known NSError **
Now finally, here is how the test looks:
#implementation StackOverFlowAnswersTests {
id<Context> context;
ContextUsingClass *sut;
ErrorSettingBlockMatcher *matcher;
}
- (void)setUp {
[super setUp];
context = mockProtocol(#protocol(Context));
sut = [[ContextUsingClass alloc] init];
sut.context = context;
matcher = [[ErrorSettingBlockMatcher alloc] init];
}
- (void)testContextResultsInError {
matcher.errorSettingBlock = ^(NSError **error) {
*error = [NSError errorWithDomain:#"dom" code:-100 userInfo:#{}];
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(YES)));
}
- (void)testContextResultsInSuccess {
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(sut.recordedError, is(equalToBool(NO)));
}
#end
Conclusion
When you call methods within your SUT which are returning errors through pointer-to-pointers, you should probably test for the different possible outcomes, rather than just verifying if the method has been called.
If your SUT is ignoring the error, then let the block you pass into the matcher keep a boolean flag to indicate that it was called like so:
- (void)testNotCaringAboutTheError {
__block BOOL called = NO;
matcher.errorSettingBlock = ^(NSError **error) {
called = YES;
};
[[given([context doWithError:nil]) withMatcher:matcher] willReturn:nil];
[sut call];
assertThatBool(called, is(equalToBool(YES)));
}
Or with simple verification:
- (void)testWithVerifyOnly {
[sut call];
[[verify(context) withMatcher:matcher] doWithError:nil];
}
PS: Ignoring errors is probably something you don't want to do...
When to copy a block? The document says, blocks are "deleted when execution returns from the scope in which they are defined.This means you can’t return them directly from a function. If blocks could only be used while their defining scope was still on the call stack, they wouldn’t be nearly as useful as they actually are"
So, here is code which I tried, hoping the block will be deleted once execution is completed in viewDidLoad.
MyReaderController.h
#interface MyReaderController : UIViewController
{
myBlockVar aBlockVar;
}
-(myBlockVar) getABlock;
#end
MyReaderController.m
#implementation MyReaderController
- (void)viewDidLoad
{
[super viewDidLoad];
aBlockVar=[self getABlock];
NSLog(#"Block Result = %f",aBlockVar(1));
}
-(void) viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
NSLog(#"Block Exists even after the execution completes=%# %f",aBlockVar,aBlockVar(5));
}
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
So, does this code require viewDidLoad to be changed to as coded below, if not then when should I use it.
- (void) viewDidLoad{
[super viewDidLoad];
aBlockVar=Block_copy([self getABlock]);
NSLog(#"Block Result = %f",aBlockVar(1));
}
PART 2
Later on I tried with this following code, hoping now it will return aBlockVar as nil obj in
viewDidDisappear.
- (void)viewDidLoad
{
[super viewDidLoad];
Blocker *blocker=[[Blocker alloc] init];
myBlockVar myVar=[blocker getABlock];
aBlockVar=myVar;
NSLog(#"Block Result = %f",aBlockVar(1));
blocker=nil;
myVar=nil;
}
Blocker.m
#import "Blocker.h"
#implementation Blocker
-(myBlockVar) getABlock{
return ^(int var){return 4.0f;};
}
#end
Are you using ARC? If so, you don't need to use Block_copy or Block_release.
If you are, then you are correct with your revised code, as Block_copy takes it off the stack and into the heap where it is has an effective retain count of 1. You would also need to call Block_release where appropriate, when finally finished with the block, to bring its balance the copy, effectively bringing the retain count back to 0.
use #property (nonatomic, copy) (int)(^myBlock)(void);
let the system do all right memory management for you!
initialize:
self.myBlock = ^int(void){
return 4.0;
};
if you want to destroy your block somewhere do self.myBlock = NULL;
An addendum to the existing answers:
Even if you're using ARC, there are certain situations where you still need Block_copy.
For example, extracting a block argument from an NSInvocation and using it after the function returns.
- (void)interceptInvocation:(NSInvocation *)call {
BlockType block;
[call getArgument:&block atIndex:2]; // ARC cannot see this happen
block = (__bridge BlockType)Block_copy((__bridge void *)block);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
block();
});
}
Without the copy, the block will have been deallocated by the time it is run.
It appears a matching Block_release is not necessary, as when I added one it crashed due to too many releases.
with arc never, without arc:
: when you have a STACK block and want to keep it as a HEAP block (e.g. when you have a block in a function and want it to live after you exited the function!)
You need to block_copy it then.
also you need retain/release it as you would a NSString so (using block_copy/block_release)