Cocoa - calling method with multiple arguments - cocoa

I'm a beginner on Cocoa and I have question about calling method with multiple arguments. I'm writing some data into bluetooth (in sync method) and wait for reply. On another method I'm trying to check that new data was received in buffer but I don't know how I can call this method. I tried many different methods and I can't :(
This method which I try to call is:
- (void) odczyt:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
{
unsigned char *dataAsBytes = (unsigned char *)dataPointer;
while ( dataLength-- )
{
[self addThisByteToTheLogs:*dataAsBytes];
dataAsBytes++;
}
}
How I should call this method? This method is responsible for reading incoming string and rewriting it to a label. Without calling this method when the button is pressed (and request for data is sent to BT) I'm not able read the incoming data - the label is empty and works after next pushing the button (when the method is exited and the main thread is initialized).
Ok - because its the same class I tried to use:
[self odczyt:IOBluetoothRFCOMMChannel *) data:(void *) length:(size_t)];
but Im getting an error - expected expression!
What I did wrong?

I think you should relook at what you want to do.
More to the point, I would post more general questions in the future.
From your title, you wanna know how to call a method with multiple prameters.
If this is correct, then:
[self someMethod:pram1 whichAlsoTake:pram2 andHasLogginStat:pram3] // if the method is part of your current class
or
[SomeClass someMethod:pram1 whichAlsoTake:pram2 andHasLogginStat:pram3] //if part of another class and its public
or
SomeClass *somethingFromAClass = [[SomeClass alloc] init];
[somethingFromAClass someMethod:pram1 whichAlsoTake:pram2 andHasLogginStat:pram3] // on an instance of a class
Hope that helps

Related

What issues could arise when using GCD dispatchAfter() in this use case

I'm going through a book on OS X programing as a refresher and have a document app set up with an array controller, tableView etc. The chapter calls for implementing undo support by hand using NSInvocation. In the chapter, they call for adding a create employee method and manually, adding outlets to the NSArrayController, and connecting my add button to the new method instead of the array controller.
Instead I did this with my method for inserting new objects:
-(void)insertObject:(Person *)object inEmployeesAtIndex:(NSUInteger)index {
NSUndoManager* undoManager = [self undoManager];
[[undoManager prepareWithInvocationTarget:self]removeObjectFromEmployeesAtIndex:index];
if (![undoManager isUndoing]) {
[undoManager setActionName:#"Add Person"];
}
[self startObservingPerson:object];
[[self employees]insertObject:object atIndex:index];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// Wait then start editing
[[self tableView]editColumn:0 row:index withEvent:nil select:YES];
});
}
This works ok (looks a bit silly), but I was wondering the what issues could arise from this. I've done this elsewhere in order to execute code after an animation finished (couldn't figure out a better way).
Thanks in advance.
Why are you delaying the invocation of -editColumn:row:withEvent:select:?
Anyway, the risks are that something else will be done between the end of this -insertObject:... method and when the dispatched task executes. Perhaps something that will change the contents of the table view such that index no longer refers to the just-added employee.

CoreMIDI call back function issue with using bridge casting on void pointer

Background: I am trying to write a program that will take MIDI information from my synthesizer and do various things with it musically. I have already connected the keyboard so that I receive MIDI data. In my midiInputCallback() method, which is the method that is called when MIDI data is generated, it takes 3 parameters: a MIDIPacketList, any object from "the outside" and the input source of the MIDI info. I'm trying to use the second parameter to pass in a UIButton. For now, I am using UIButtons for the piano keys. When I play on my synth, I want it to show (on my GUI) that a certain note has been played. To show this, the button that is associated with the key on my keyboard will appear "pressed".
Problem: Just to test this process, I tried passing in a NSNumber called test.
- (void)awakeFromNib {
MIDIClientRef midiClient;
checkError(MIDIClientCreate(CFSTR("MIDI client"), NULL, NULL, &midiClient), "MIDI client creation error");
test = [NSNumber numberWithInt:4];
MIDIPortRef inputPort;
checkError(MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, &test, &inputPort), "MIDI input port error");
unsigned long sourceCount = MIDIGetNumberOfSources();
for (int i = 0; i < sourceCount; ++i) {
MIDIEndpointRef endPoint = MIDIGetSource(i);
CFStringRef endpointName = NULL;
checkError(MIDIObjectGetStringProperty(endPoint, kMIDIPropertyName, &endpointName), "String property not found");
checkError(MIDIPortConnectSource(inputPort, endPoint, NULL), "MIDI not connected");
}
}
I pass test into MIDIInputPortCreate() which sets up the callback method as midiInputCallback().
Here's what midiInputCallBack() looks likes:
static void midiInputCallback(const MIDIPacketList *list, void *procRef, void *srcRef) {
NSNumber *test = (__bridge_transfer NSNumber*)procRef;
NSLog(#"%#", test);
...
I tried regular casting at first: NSNumber test = (NSNUmber) procRef, but then XCode said to use some bridge casting instead. I've read up on this a bit and someone suggested to use __bridge_transfer instead of just __bridge. Each time I run this I get a EXC_BAD_ACCESS(code=1, address=0x43f) error for my NSLog() in midiInputCallback() and I'm not sure how to proceed. Let me know if you need more information. I'm fairly new to developing for the Mac. Thanks in advance!
Had the same problem some time ago when writing a similar app that receives MIDI input and logs it to the console.
Am assuming you are working with ARC.
Have solved the problem as follows:
First, I did the following cast (that would be in your awakeFromNib method, whereas &test would be some_object):
SomeClass *some_object = [[SomeClass alloc] init];
MIDIInputPortCreate(midiClient, CFSTR("Input"), midiInputCallback, (__bridge_retained void *)some_object, &inputPort);
Then, inside midiInputCallback, I have casted it back with:
SomeClass *some_object = (__bridge SomeClass*)procRef;
Hope I don't have any typos here, just did a quick copy and paste with some modifications for the context of this answer.
Checkout the following question and its answers, helped me a lot understanding it:
ARC and bridged cast

performSelector:withObject:afterDelay: - a way to do this to pass something other than an object?

I am trying to use this method to show a second modal after a first modal has been dismissed... The method itself works fine, except that it requires me to pass an object as a parameter and what I really want to do is pass a UIImageControllerSourceType instead. Is there another method I should be using to do this or is there an easy way to make UIImageControllerSourceType an object? I feel like this should not be too hard but I have been struggling with it for awhile now...
- (void)showModalTwoImageSearchViewControllerWithSourceType:(UIImagePickerControllerSourceType *)sourceType {
if (self.modalViewController) {
[self performSelector:#selector(showModalTwoImageSearchViewControllerWithSourceType:)
withObject:sourceType
afterDelay:0.1f];
return;
}
// present second modal view - remembering to deal with cases of camera or photo library
if (sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {
//do one thing
}
// lets assume it was UIImagePickerControllerSourceTypeCamera
else {
// do something else
}
}
UIImagePickerControllerSourceType is ultimately a NSUInteger and the method you're trying to do (the performSelector one) only takes Objective C objects in its withObject parameter and not C types or pointers.
You can pass NSUIntegers by creating a NSNumber object and stuffing your integer into that.
To create a NSNumber object, you can create one as easily as NSNumber's numberWithUnsignedInteger method.
UIImagePickerControllerSourceType is an enum. the easiest way to use it as an object is to make it an NSNumber:
NSNumber *n = [NSNumber numberWithInt:sourceType];

Difference between mutableArrayValueForKey and calling insertObject:inEmployeesAtIndex: directly

I have a question regarding using KVO-compliant methods to insert/remove objects from an array. I'm working through Aaron Hillegass' Cocoa Programming for Mac OS X and I saw the following line of code (in the insertObject:inEmployeesAtIndex: method:
[[undoManager prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index];
Correct me if I'm wrong, but I always thought it was better to call mutableArrayValueForKey: and then removeObjectAtIndex:...so I tried changing the above line to this:
[[undoManager prepareWithInvocationTarget:[self mutableArrayValueForKey:#"employees"]] removeObjectAtIndex:index];
And it didn't work. Can someone explain the difference and why the first line works but the second line doesn't?
UPDATE: My removeObjectFromEmployeesAtIndex:index method is implemented to make my collection class (an instance of NSMutableArray) KVC-compliant. So ultimately, calling [[self mutableArrayValueForKey:#"employees"] removeObjectAtIndex:index]; should end up calling [self removeObjectFromEmployeesAtIndex:index];
In your update you say:
calling [[self mutableArrayValueForKey:#"employees"] removeObjectAtIndex:index]; should end up calling [self removeObjectFromEmployeesAtIndex:index];
Unfortunately this is not correct not matter what is in your removeObjectFromEmployeesAtIndex: method as NSMutableArray will never call any methods in your class. Since you seem to be trying to get undo/redo functionality you have to use a method like removeObjectFromEmployeesAtIndex:. Otherwise when you hit undo for adding an employee you will have no way to 'redo' adding that employee. You also could have issues with undo/redo for edits to individual employees. If you wanted to you could change the line in the removeObjectFromEmployeesAtIndex: method that reads [employees removeObjectAtIndex:index]; to [[self valueForKey:#"employees"] removeObjectAtIndex:index]; or [self.employees removeObjectAtIndex:index]; but there is really no reason to go this route.
Yes. The first line (from the book) is basically equivalent to this:
id tmp = [undoManager prepareWithInvocationTarget:self];
[tmp removeObejctFromEmployeesAtIndex:index];
Your code, however, is basically equivalent to this:
id tmp1 = [self mutableArrayValueForKey:#"employees"];
id tmp2 = [undoManager prepareWithInvocationTarget:tmp1];
[tmp2 removeObjectAtIndex:index];
In other words, the target that you're preparing the invocation with is different in your code (unless self happens to be the same object as [self mutableArrayValueForKey:#"employees"], which is doubtful).

How to subclass AtlasSpriteManager in Cocos2d?

I need to create some compound sprites that will all move and rotate together. Since it's possible to change the position and rotation of an AtlasSpriteManager I've been trying to subclass so I can create a bunch of shortcuts like
CompoundSprite *cSprite = [CompoundSprite spriteManagerWithFile:#"sprites.png"];
[cSprite makeComplexSprite];
internally, it looks a little like this
-(void)makeComplexSprite
{
AtlasSprite *sp1 = [[AtlasSprite spriteWithRect:CGRectMake(0, 0, 64, 64)
spriteManager:self] retain];
AtlasSprite *sp2 = [[AtlasSprite spriteWithRect:CGRectMake(0, 0, 64, 64)
spriteManager:self] retain];
[self addChild:sp1];
[self addChild:sp2];
[sp1 setPosition:CGPointMake(0,0)];
[sp2 setPosition:CGPointMake(64,0)];
}
However, when I run the applications, It crashes with the following exception
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[AtlasSpriteManager makeComplexSprite]: unrecognized selector sent to
instance 0x107e1c0
Also, if I remove all the code inside 'MakeComplexSprite' and make it do nothing, I also get the same problem.
It's looking like AtlasSpriteManager just doesn't like to be sub classed. Is this the case? If so, why, and how could I work around it?
UPDATE:
I've found a workaround, by creating an NSObject that contains an atlasSpriteManager. It does the trick, but I would still like to subclass AtlasSpriteManager if possible. I appear to be implementing this exaclty as you describe. I'm creating an instance like this
CompoundSprite *cSprite = [CompoundSprite spriteManagerWithFile:#"file.png"];
[cSprite makeBox];
which... now I think about it, means that cSprite is still an AtlasSpriteManager since that's what is being returned. hmmmm. Ho do I change that?
Implement your own spriteManagerWithFile: or compoundSpriteWithFile: in CompoundSprite, which will return an instance of CompoundSprite.
Edit:
Or, you can do something like
[[ComplexSprite alloc] makeComplexSprite];
But then you need to do the 'spriteManagerWithFile:' part also. Like:
-(id)makeComplexSpriteWithFile:(NSString*)file
{
if (! (self = [super initWithSpriteManager:..capacity:..]))
return nil;
// do your ComplexSprite specific initializing here..
return self;
}
The runtime error you are seeing indicates that your program has tried to send the makeComplexSprite message to an object, but no such method has been defined for that object.
You appear to be sending the makeComplexSprite message to an instance of AtlasSpriteManager instead of an instance of your custom CompoundSprite class. Your example code looks correct, so how are you doing the subclassing? It should look something like this:
CompoundSprite.h:
#interface CompoundSprite : AtlasSpriteManager
{
}
- (void)makeComplexSprite;
#end
CompoundSprite.m:
#interface CompoundSprite
- (void)makeComplexSprite
{
...
}
#end
If you do have the subclassing set up properly, make sure you are actually calling makeComplexSprite on an instance of CompoundSprite and not some other object by accident.
Also:
Your code sample has a memory leak. You are creating two autoreleased sprites, then retaining them (which means your class takes ownership of them), and never releasing them. Since the AddChild: method will automatically retain the objects, you can simply lose the retain calls, and everything will be good.

Resources