Since the way to instantiate an AVAudioUnit is this:
[AVAudioUnit instantiateWithComponentDescription:componentDescription options:0 completionHandler:^(__kindof AVAudioUnit * _Nullable audioUnit, NSError * _Nullable error) {
}];
How am I supposed to subclass AVAudioUnit? I have tried this:
[MySubclassOfAVAudioUnit instantiateWithComponentDescription:componentDescription options:0 completionHandler:^(__kindof AVAudioUnit * _Nullable audioUnit, NSError * _Nullable error) {
}];
But the audioUnit that is returned in the block is still of type AVAudioUnit and NOT MySubclassOfAVAudioUnit.
Per Rhythmic Fistman's response, I am registering my custom AUAudioUnit subclass with Apple's example code:
componentDescription.componentType = kAudioUnitType_Effect;
componentDescription.componentSubType = 0x666c7472; /*'fltr'*/
componentDescription.componentManufacturer = 0x44656d6f; /*'Demo'*/
componentDescription.componentFlags = 0;
componentDescription.componentFlagsMask = 0;
I want my AVAudioUnit subclass to always use my AUAudioUnit.
From instantiateWithComponentDescription:completionHandler:
The returned AVAudioUnit instance normally will be of a subclass (AVAudioUnitEffect,
AVAudioUnitGenerator, AVAudioUnitMIDIInstrument, or AVAudioUnitTimeEffect), selected
according to the component's type.
UPDATE
I got this wrong - you can't instantiate your own AVAudioUnit subclass, you can only instantiate your AUAudioUnit, wrapped in the relevant built-in AVFoundation AVAudioUnit subclass (e.g. AVAudioUnitEffect, etc).
The following code causes MyAUAudioUnit, a subclass of AUAudioUnit to be instantiated:
#import <AVFoundation/AVFoundation.h>
#interface MyAUAudioUnit : AUAudioUnit {
}
#end
#implementation MyAUAudioUnit
// implement it here
#end
// later
- (void)instantiateMyAUAudioUnitWrappedInAVAudioUnit {
// register it (need only be done once)
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Effect;
desc.componentSubType = 0x666c7472; /*'fltr'*/
desc.componentManufacturer = 0x44656d6f; /*'Demo'*/
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
[AUAudioUnit registerSubclass:MyAUAudioUnit.class asComponentDescription:desc name:#"MyAU" version:1];
// Instantiate as many times as you like:
[AVAudioUnit instantiateWithComponentDescription:desc options:0 completionHandler:^(AVAudioUnit * audioUnit, NSError *error) {
NSLog(#"AVAudioUnit: %#, error: %#", audioUnit, error);
}];
}
WRONG BIT
So to have your AVAudioUnit subclass instantiated, you must first register it with the AUAudioUnit method:
+[AUAudioUnit registerSubclass:asComponentDescription:name:version:]
There is a code snippet and some possible gotchas in this devforum thread.
Related
I've created a class to control an NSOutlineView and that class comunicate with my AppDelegate using notification. This class that controls the behavior of the outlineview is initialized using awakefromnib so that header is added immediately (my will), while later by calling a method of this class is populated by children. Everything works ok, but when is the moment to create a notification for my AppleDelegate I discover that instance variable is null while at the time of the initial call was ok.
#interface MyClass : NSObject <NSApplicationDelegate, NSTextViewDelegate, NSTextFieldDelegate, NSOutlineViewDelegate, NSOutlineViewDataSource>
{
NSString * _plistPath;
}
#implementation MyClass
- (void)awakeFromNib {
static dispatch_once_t once;
dispatch_once(& once, ^{
self.Outline.delegate = self;
self.Outline.dataSource = self;
self.Outline.floatsGroupRows = NO;
[_treeController addObject: #{#"title": #"Model list", #"isLeaf": #(NO)}.mutableCopy];
[self.Outline expandItem:[self.Outline itemAtRow:0]];
[self.Outline selectRowIndexes:[NSIndexSet indexSetWithIndex:1] byExtendingSelection:NO];
// Enable Drag and Drop
[self.Outline registerForDraggedTypes: [NSArray arrayWithObject: #"public.text"]];
});
}
- (void)loadPlist:(NSDictionary *)dict path:(NSString *)path
{
_plistPath = path;
NSLog(#"at loadPlist _plistPath = %#, self is = %#", _plistPath, self); // here is ok!
}
// Here all the NSOutlineViewDelegate methods
// .....
// finally after editing value in the interface using the outline view
// and some field attacched to the tree controller..
// I need to notificate AppleDelegate for the changes made to the plist,
//reindicating the path stored in _plistPath variable..but:
- (void)update
{
NSMutableArray *List = [NSMutableArray array];
NSInteger rows = [_Outline numberOfRows];
NSInteger i;
i = 0;
while (i != rows) {
id obj = [_Outline itemAtRow:i];
if (i != 0) {
Tree *entry = (Tree *)(((NSTreeNode *)obj).representedObject);
if (entry.title.length > 0 && ![entry.title isEqualToString:kNullString]
&& entry.size.length > 0 && ![entry.size isEqualToString:kNullString]
&& entry.model.length > 0 && ![entry.model isEqualToString:kNullString]
&& entry.year.length > 0 && ![entry.year isEqualToString:kNullString]) {
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict setObject:entry.title forKey:#"Name"];
[childDict setObject:entry.size forKey:#"Size"];
[childDict setObject:entry.model forKey:#"Model"];
[childDict setObject:entry.year forKey:#"Year"];
[List addObject:childDict];
}
}
i++;
}
NSMutableDictionary *uf = [NSMutableDictionary dictionary];
NSLog(#"at update _plistPath = %#, self is = %#", _plistPath, self); // this time here is nil....
[uf setObject:_plistPath forKey:#"path"]; // here crash because _plistPath is nil
[uf setObject:List forKey:#"dictionary"];
NSNotification *note = [NSNotification notificationWithName:#"UpdatePlist" object:_Outline userInfo:uf];
[[NSNotificationCenter defaultCenter] postNotification:note];
}
basically at the second time I need "_plistPath" value is nil (I have some very specific reasons for sending to MyClass the original path and then return it back to the sender (ie AppleDelegate)) and I have more instance variables declared in MyClass that works well..so I can't understand why. Any suggestion?
EDIT
As requested by #matt I change the NSLog including "self"
then the output is that:
2015-05-11 22:06:36.433 TestApp[24990:56140] at loadPlist _plistPath = /Volumes/DATI/test.plist, self is = <MyClass: 0x600000127760>
2015-05-11 22:07:08.552 TestApp[24990:56140] at update _plistPath = (null), self is = <MyClass: 0x608000125c80>
Your log shows the problem:
at loadPlist _plistPath = /Volumes/DATI/test.plist,
self is = <MyClass: 0x600000127760>
at update _plistPath = (null),
self is = <MyClass: 0x608000125c80>
These are two difference instances of MyClass. But plistPath is an instance variable - so it can perfectly reasonably have a value in one instance and be nil in another instance.
I am using RestKit version 0.20.3 to make a generic method that is used in many other places. The problem is the returned value from that method is always nil because the "return location;" statement is executed BEFORE the Success call back function over the [objectManager getObjectsAtPath ...] method (see below codes).
I want the "return location;" statement must WAIT for the block variable "location" is filled with data from the Success call back function inside the [objectManager getObjectsAtPath ...] method. How can I do this?
Thank you for your help.
My generic method looks like:
-(KNSunGoogleLatitudeLongitudeGeometryLocation*)getSynchronouslyLatitudeLongitudeWithAddress:(NSString*)address
{
__block KNSunGoogleLatitudeLongitudeGeometryLocation* location = [[KNSunGoogleLatitudeLongitudeGeometryLocation alloc] init];
NSURL *baseURL = [NSURL URLWithString:#"http://maps.googleapis.com/maps/api"];
AFHTTPClient * client = [AFHTTPClient clientWithBaseURL:baseURL];
[client setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
//1. KNSunGoogleLatitudeLongitudeGeometryLocation
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitudeGeometryLocation class]];
[locationMapping addAttributeMappingsFromArray:#[#"lat", #"lng"]];
//2. KNSunGoogleLatitudeLongitudeGeometry
RKObjectMapping *geometryMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitudeGeometry class]];
//3. KNSunGoogleLatitudeLongitude
RKObjectMapping *latLongMapping = [RKObjectMapping mappingForClass:[KNSunGoogleLatitudeLongitude class]];
//4. property/relationship mapping
[geometryMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"location"
toKeyPath:#"location"
withMapping:locationMapping]];
[latLongMapping addPropertyMapping:[RKRelationshipMapping
relationshipMappingFromKeyPath:#"geometry"
toKeyPath:#"geometry"
withMapping:geometryMapping]];
// 6. response
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:latLongMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"results"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
// 7
[objectManager addResponseDescriptor:responseDescriptor];
NSDictionary *queryParams;
queryParams = [NSDictionary dictionaryWithObjectsAndKeys:address, #"address", #"false", #"sensor", nil];
// 6
[objectManager getObjectsAtPath:#"http://maps.googleapis.com/maps/api/geocode/json"
parameters:queryParams
success:^(RKObjectRequestOperation * operaton, RKMappingResult *mappingResult)
{
//-----------
NSArray* results = [mappingResult array];
KNSunGoogleLatitudeLongitude* result0 = [results objectAtIndex:0];
KNSunGoogleLatitudeLongitudeGeometry* geometry = result0.geometry;
location= geometry.location;
NSLog(#"lat=%#, long=%#", location.lat, location.lng);
}
failure:^(RKObjectRequestOperation * operaton, NSError * error)
{
NSLog (#"failure: operation: %# \n\nerror: %#", operaton, error);
}];
return location; // note: ALWAYS RETURNs nil
}
You need to change what you want because it's a bad design. You should not block the requestor while the request is in progress. Instead you should pass a block to your general method that is executed from the block you pass to RestKit. This allows you to properly respect the asynchronous nature of the request.
If you did want to proceed with blocking, you could use look at using a semaphore. But, you would need to manage this yourself. And you wouldn't be able to trigger the request on the main thread - ever. These are significant hurdles to general usage and will probably cause you issues in the future.
I have a problem code with using AFNetworking:
#import "SyncProfile.h"
#import "AFNetworking.h"
#implementation SyncProfile: NSObject
#synthesize delegate = _delegate;
- (BOOL)syncProfile {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *token =[userDefaults objectForKey:#"token"];
int user_id = [userDefaults objectForKey:#"user_id"];
if([token length]) {
self.profileData = [[self sendRequest:#"method.get" token:token withUser:user_id andParameters:#"param1,param2"] valueForKeyPath:#"response"];
NSLog(#"%#", self.profileData);
return YES;
} else
return NO;
}
-(id)sendRequest:(NSString *)apiMethod token:(NSString *)token withUser:(int)user_id andParameters:(NSString *)param {
NSMutableString *apiLink = [NSMutableString stringWithFormat:#"https://domain.com/method/%#?uid=%#&fields=%#&access_token=%#", apiMethod, user_id, param, token];
NSURL *url = [[NSURL alloc] initWithString:apiLink];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(#"%#", JSON);
self.req = JSON;
[self myMethod:JSON];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(#"Request Failed with Error: %#, %#", error, error.userInfo);
}];
[operation start];
return self.req;
}
- (id)myMethod:(id)data {
NSLog(#"%#",data);
return 0;
}
#end
I need to return a variable with the result AFNetworking back method. But the result is given much later than the method returns. When I use a different method to handle the result, it does not. Tried to use the [operation waitUntilFinished] but nothing has changed.
Result in Xcode Output:
//Return variable from "sync" method
2013-02-26 23:57:29.793 walkwithme[13815:11303] (null)
//Return from AFN response
2013-02-26 23:57:31.063 walkwithme[13815:11303] {response = ({someJSON})}
//Return from MyMethod
2013-02-26 23:57:31.063 walkwithme[13815:11303] {response = ({someJSON})}
You definitely don't want to use any wait methods. What you need to do is have a call back in your success and failure blocks. You can do this the way I showed in this question you could also do something else like message passing. The key thing to realize is you won't be using the typical method return pattern. Reason being with asynchronous methods like this you have no idea when it will finish which is why it uses the block call backs. Like I said you definitely don't want to wait because that could entirely block your application.
EDIT:
I use this code in one of my projects:
Declare method
- (void)postText:(NSString *)text
forUserName:(NSString *)username
ADNDictionary:(NSDictionary *)dictionary
withBlock:(void(^)(NSDictionary *response, NSError *error))block;
Then inside this method I pass those parameters to the network request.
Return values in the block with:
if (block) {
block(responseObject, someError);
}
Then I call it with this:
[[KSADNAPIClient sharedAPI] postText:postText
forUserName:username
ADNDictionary:parameters
withBlock:^(NSDictionary *response, NSError *error)
{
if (error) {
// Deal with error
} else {
// Probably success!
}
}
This way the called method returns it's values to the caller method inside the block. I think about it that it defers the block to the caller.
In my OS X app, I'm trying to save and retrieve the tag of a radio button. The error occurs on the line marked "<-HERE" in setPreferenceRotor. There is a valid tag coming in.
// PreferenceController.h
extern NSString * const myCellKey;
extern NSString * const myMatrixChangedNotification;
#interface PreferenceController:NSWindowController
{
IBOutlet NSMatrix *matrixRotor;
}
- (IBAction)setRotorTag:(id)sender;
+ (NSInteger)preferenceRotorTag;
+ (void)setPreferenceRotor:(NSInteger)matrixTag;
#end
// PreferenceController.m
NSString * const myMatrixChangedNotification = #"myRotorChanged";
#implementation PreferenceController
- (void)windowDidLoad
{
[super windowDidLoad];
[matrixRotor selectCellWithTag:[PreferenceController preferenceRotorTag]];
}
+ (NSInteger)preferenceRotorTag
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *tagAsData = [defaults objectForKey:myCellKey];
return [NSKeyedUnarchiver unarchiveObjectWithData:tagAsData];
}
+ (void)setPreferenceRotor:(NSInteger)matrixTag
{
//NSInteger mt = matrixTag;
NSData *tagAsData = [NSKeyedArchiver archivedDataWithRootObject:matrixTag]; **//<-HERE**
[[NSUserDefaults standardUserDefaults]setObject:tagAsData forKey:myCellKey];
}
You are passing a primitive (non-object) value, of type NSInteger from variable matrixTag, to a method, archivedDataWithRootObject:, which expects an object reference value. That method happily tries to use the value (which is probably the integer 16, 0x10) as an object reference, and kaboom...
Your thinking looks correct, you know you cannot store non-object values in user defaults, and so you must wrap them as objects first. It is just your way of doing so that is wrong. What you need here is to create an instance of NSNumber from your integer. You could write:
NSNumber *tagAsNumber = [NSNumber numberWithInteger:matrixTag];
[[NSUserDefaults standardUserDefaults] setObject:tagAsNumber forKey:myCellKey];
However this pattern is common enough that a shortcut is provided:
[[NSUserDefaults standardUserDefaults] setInteger:matrixTag forKey:myCellKey];
and this will create the NSNumber object for you. There is also a corresponding integerForKey: method which will unwrap the integer for you when reading.
I have a view controller that is a subclass of UITableViewController. Here is my viewWillAppear:animated method:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (fetchedResultsController != nil) {
[fetchedResultsController release];
fetchedResultsController = nil;
}
[self.fetchedResultsController performFetch:nil];
[self.tableView reloadData];
}
I am getting confused by seeing the fetchedResultsController being accessed when [super viewWillAppear:animated] is called. Since super is a UITableViewController, and there is no viewWillAppear:animated method for that class, per se, then its superclass viewWillAppear:animated should be called, right? If that's correct, then the UIViewController class should not be accessing UITableViewController delegate methods. But I see that numberOfSectionsInTableView is getting called. I'm not sure why the call to super viewWillAppear:animated would do this.
So before I explicitly run the peformFetch and reloadData, the table is getting populated. At that time, the data it is being populated with is out of date.
Here is the fetchedResultsController code
- (NSFetchedResultsController *) fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSFetchRequest *fetchRequest = ...
NSEntityDescription * entity = ...
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:10];
NSSortDescriptor *aSortDescriptor = ...
NSSortDescriptor *bSortDescriptor = ...
NSArray *sortDescriptors = ...
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = ...
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
...
[sortDescriptors release];
NSError *error = nil;
if (![fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved Error %#, %#", error, [error userInfo]);
abort();
}
return fetchedResultsController;
}
The documentation specifically describes this behaviour:
When the table view is about to appear the first time it’s loaded, the table-view controller reloads the table view’s data. It also clears its selection (with or without animation, depending on the request) every time the table view is displayed. The UITableViewController class implements this in the superclass method viewWillAppear:. You can disable this behavior by changing the value in the clearsSelectionOnViewWillAppear property.