Stuck in loop form CFRunLoop - ios

ViewController.h
#interface ViewController : UIViewController{
CFSocketRef s;
int connectFlag;
}
ViewController.m
void receiveDataCilent(CFSocketRef cs,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
CFDataRef df = (CFDataRef) data;
int len = CFDataGetLength(df);
if(len <= 0) return;
CFRange range = CFRangeMake(0,len);
UInt8 buffer[len];
NSLog(#"Received %d bytes from socket %d\n",
len, CFSocketGetNative(cs));
CFDataGetBytes(df, range, buffer);
NSLog(#"Client received: %s\n", buffer);
NSLog(#"As UInt8 coding: %#", df);
NSLog(#"len value: %d", len);
}
-(void) clientConnect:(int)sender;{
s = CFSocketCreate(NULL, PF_INET,
SOCK_STREAM, IPPROTO_TCP,
kCFSocketDataCallBack,
receiveDataCilent,
NULL);
struct sockaddr_in sin;
struct hostent *host;
memset(&sin, 0, sizeof(sin));
host = gethostbyname("localhost");
memcpy(&(sin.sin_addr), host->h_addr,host->h_length);
sin.sin_family = AF_INET;
sin.sin_port = htons(6666);
CFDataRef address;
CFRunLoopSourceRef source;
address = CFDataCreate(NULL, (UInt8 *)&sin, sizeof(sin));
CFSocketConnectToAddress(s, address, 0);
CFRelease(address);
source = CFSocketCreateRunLoopSource(NULL, s, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
source,
kCFRunLoopDefaultMode);
CFRelease(source);
CFRunLoopRun();
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(connectFlag == 0 ){
[self clientConnect:1];
}
}
First I'm sorry for messing the concept of xcode programming and threading programming I'm very new to xcode and I need to finish my game project soon
This code is suppose to connect to server and waiting for message from server. I try and test it and it work fine. In view I have IBAction that can press and change view.
But after I implement it to my main project. It can run but I cannot press any button or do anything. After log it out. It seem it stuck around CFRunLoopRun(); I think it stuck in loop but why when I test it. it not stuck ?
In my project this code is run from another view not from ViewController like I just test.
I don't know why it stuck in my project.
Any help would be appreciated.

Don't run the run loop yourself on the main thread. The main event loop will run it.
Edit: I should say, "within the context of a GUI app". In a command-line tool, you do need to run the run loop yourself.

Related

Main Thread blocking NSStream when checking if input is available on NSStream

I am working with a package called DSBridge to connect a mobile application (iOS code in this case) to javascript code containing the main logic of my application. This involves running the JavaScript on an invisible WKWebView.
My JavaScript code needs to call a method in the native iOS application which needs to execute and return a value to the JavaScript.
My Objective C function reads:
- (NSString* )read:(NSDictionary *) args{
NSLog(#"In Read");
while(self->connection != NULL) {
if([self->connection isMessageAvailable]) {
[self->connection messageRetrieved];
NSLog(#"Message Received in ViewController");
return [self->connection getMessage];
}
}
return NULL;
}
Which is called from my JavaScript, I need this to return on the main thread so that the return value is sent into my WebView.
The issue is that my the message from IO I am waiting for will never appear as my NSStream is blocked by the while loop.
My connection methods and NSStream look like this:
-(void)readData:(NSData*)receivedData{
NSData *decoded = [self btXOR:receivedData withMask:0x26];
message = [[NSString alloc] initWithData:decoded encoding:NSUTF8StringEncoding];
NSLog(#"message: %#", message);
messageAvailable = true;
}
- (BOOL) isMessageAvailable {
return messageAvailable;
}
- (void) messageRetrieved {
messageAvailable = false;
}
- (NSString*) getMessage {
NSLog(#"Message: %#", message);
return message;
}
#pragma mark StreamDelegate
-(void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode)
{
//Reading in from host done here
case NSStreamEventHasBytesAvailable:
DDLogInfo(#"NSStreamEventHasBytesAvailable");
DDLogInfo(#"stream is input: %i " , (theStream == inputStream));
NSLog(#"NSStreamEventHasBytesAvailable");
NSLog(#"stream is input: %i " , (theStream == inputStream));
if (theStream == inputStream)
{
uint8_t buffer[1024];
long len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
DDLogDebug(#"InputStream still has bytes");
NSLog(#"InputStream still has bytes");
if (len > 0)
{
NSMutableData* data=[[NSMutableData alloc] initWithLength:0];
[data appendBytes: (const void *)buffer length:(int)len];
[self readData:data];
}
}
}
break;
The 'NSStreamEventHasBytesAvailable' case should occur once the message is ready, and should be picked up by my read function so it can be returned to my WebViews JavaScript.
How can I take the while loop off of the main thread and still return once I have the message? Is it possible to take the NSStream off the main runloop in a way that it will not be blocked by my read function?
I tried making read async and using a callback to the JavaScript as seen here, but this makes my JavaScript codebase rather unwieldy with callbacks.

UI processing causes audio glitches - should audio play on background thread?

When you rotate the device, my app has quite a bit of logic to lay out the view differently for portrait/landscape - e.g. remove views, change frame sizes, redraw views, switch images etc.
Works flawlessly on new devices. On older devices if this is done while audio is playing it glitches considerably.
Is this most likely because the audio code is being blocked by the UI? Should my audio play on a background thread? The CPU even on an iPhone 4 is only about 20% during this transition so I don't it's a CPU issue.
I am using the loadPresetDemo example of AUSampler to play audio and my thread looks like:
-(void)start
{
playing = YES;
if([NSThread isMainThread]) {
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(timerStart) object:nil];
[thread start];
return;
}
[[NSThread currentThread] setThreadPriority:1.0];
}
-(void)timerStart
{
NSRunLoop *timerRunLoop = [NSRunLoop currentRunLoop];
timer = [NSTimer scheduledTimerWithTimeInterval:intervalInMs/1000
target:self
selector:#selector(beat)
userInfo:nil
repeats:YES];
[timerRunLoop run];
}
- (void)beat
{
if(playing) {
[audioPlayer beat];
//UI thread
dispatch_async(dispatch_get_main_queue(), ^{
[mView setBeat:audioPlayer.currentBeat];
});
}
}
AUSampler:
#import "MainViewController.h"
#import <AssertMacros.h>
// some MIDI constants:
enum {
kMIDIMessage_NoteOn = 0x9,
kMIDIMessage_NoteOff = 0x8,
};
#define kLowNote 48
#define kHighNote 72
#define kMidNote 60
// private class extension
#interface MainViewController ()
#property (readwrite) Float64 graphSampleRate;
#property (readwrite) AUGraph processingGraph;
#property (readwrite) AudioUnit samplerUnit;
#property (readwrite) AudioUnit ioUnit;
- (OSStatus) loadSynthFromPresetURL:(NSURL *) presetURL;
- (void) registerForUIApplicationNotifications;
- (BOOL) createAUGraph;
- (void) configureAndStartAudioProcessingGraph: (AUGraph) graph;
- (void) stopAudioProcessingGraph;
- (void) restartAudioProcessingGraph;
#end
#implementation MainViewController
#synthesize graphSampleRate = _graphSampleRate;
#synthesize currentPresetLabel = _currentPresetLabel;
#synthesize presetOneButton = _presetOneButton;
#synthesize presetTwoButton = _presetTwoButton;
#synthesize lowNoteButton = _lowNoteButton;
#synthesize midNoteButton = _midNoteButton;
#synthesize highNoteButton = _highNoteButton;
#synthesize samplerUnit = _samplerUnit;
#synthesize ioUnit = _ioUnit;
#synthesize processingGraph = _processingGraph;
#pragma mark -
#pragma mark Audio setup
// Create an audio processing graph.
- (BOOL) createAUGraph {
OSStatus result = noErr;
AUNode samplerNode, ioNode;
// Specify the common portion of an audio unit's identify, used for both audio units
// in the graph.
AudioComponentDescription cd = {};
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
// Instantiate an audio processing graph
result = NewAUGraph (&_processingGraph);
NSCAssert (result == noErr, #"Unable to create an AUGraph object. Error code: %d '%.4s'", (int) result, (const char *)&result);
//Specify the Sampler unit, to be used as the first node of the graph
cd.componentType = kAudioUnitType_MusicDevice;
cd.componentSubType = kAudioUnitSubType_Sampler;
// Add the Sampler unit node to the graph
result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode);
NSCAssert (result == noErr, #"Unable to add the Sampler unit to the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Specify the Output unit, to be used as the second and final node of the graph
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_RemoteIO;
// Add the Output unit node to the graph
result = AUGraphAddNode (self.processingGraph, &cd, &ioNode);
NSCAssert (result == noErr, #"Unable to add the Output unit to the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Open the graph
result = AUGraphOpen (self.processingGraph);
NSCAssert (result == noErr, #"Unable to open the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Connect the Sampler unit to the output unit
result = AUGraphConnectNodeInput (self.processingGraph, samplerNode, 0, ioNode, 0);
NSCAssert (result == noErr, #"Unable to interconnect the nodes in the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Obtain a reference to the Sampler unit from its node
result = AUGraphNodeInfo (self.processingGraph, samplerNode, 0, &_samplerUnit);
NSCAssert (result == noErr, #"Unable to obtain a reference to the Sampler unit. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Obtain a reference to the I/O unit from its node
result = AUGraphNodeInfo (self.processingGraph, ioNode, 0, &_ioUnit);
NSCAssert (result == noErr, #"Unable to obtain a reference to the I/O unit. Error code: %d '%.4s'", (int) result, (const char *)&result);
return YES;
}
// Starting with instantiated audio processing graph, configure its
// audio units, initialize it, and start it.
- (void) configureAndStartAudioProcessingGraph: (AUGraph) graph {
OSStatus result = noErr;
UInt32 framesPerSlice = 0;
UInt32 framesPerSlicePropertySize = sizeof (framesPerSlice);
UInt32 sampleRatePropertySize = sizeof (self.graphSampleRate);
result = AudioUnitInitialize (self.ioUnit);
NSCAssert (result == noErr, #"Unable to initialize the I/O unit. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Set the I/O unit's output sample rate.
result = AudioUnitSetProperty (
self.ioUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output,
0,
&_graphSampleRate,
sampleRatePropertySize
);
NSAssert (result == noErr, #"AudioUnitSetProperty (set Sampler unit output stream sample rate). Error code: %d '%.4s'", (int) result, (const char *)&result);
// Obtain the value of the maximum-frames-per-slice from the I/O unit.
result = AudioUnitGetProperty (
self.ioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&framesPerSlice,
&framesPerSlicePropertySize
);
NSCAssert (result == noErr, #"Unable to retrieve the maximum frames per slice property from the I/O unit. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Set the Sampler unit's output sample rate.
result = AudioUnitSetProperty (
self.samplerUnit,
kAudioUnitProperty_SampleRate,
kAudioUnitScope_Output,
0,
&_graphSampleRate,
sampleRatePropertySize
);
NSAssert (result == noErr, #"AudioUnitSetProperty (set Sampler unit output stream sample rate). Error code: %d '%.4s'", (int) result, (const char *)&result);
// Set the Sampler unit's maximum frames-per-slice.
result = AudioUnitSetProperty (
self.samplerUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&framesPerSlice,
framesPerSlicePropertySize
);
NSAssert( result == noErr, #"AudioUnitSetProperty (set Sampler unit maximum frames per slice). Error code: %d '%.4s'", (int) result, (const char *)&result);
if (graph) {
// Initialize the audio processing graph.
result = AUGraphInitialize (graph);
NSAssert (result == noErr, #"Unable to initialze AUGraph object. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Start the graph
result = AUGraphStart (graph);
NSAssert (result == noErr, #"Unable to start audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
// Print out the graph to the console
CAShow (graph);
}
}
// Load the Trombone preset
- (IBAction)loadPresetOne:(id)sender {
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Trombone" ofType:#"aupreset"]];
if (presetURL) {
NSLog(#"Attempting to load preset '%#'\n", [presetURL description]);
self.currentPresetLabel.text = #"Trombone";
}
else {
NSLog(#"COULD NOT GET PRESET PATH!");
}
[self loadSynthFromPresetURL: presetURL];
}
// Load the Vibraphone preset
- (IBAction)loadPresetTwo:(id)sender {
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Vibraphone" ofType:#"aupreset"]];
if (presetURL) {
NSLog(#"Attempting to load preset '%#'\n", [presetURL description]);
self.currentPresetLabel.text = #"Vibraphone"; }
else {
NSLog(#"COULD NOT GET PRESET PATH!");
}
[self loadSynthFromPresetURL: presetURL];
}
// Load a synthesizer preset file and apply it to the Sampler unit
- (OSStatus) loadSynthFromPresetURL: (NSURL *) presetURL {
CFDataRef propertyResourceData = 0;
Boolean status;
SInt32 errorCode = 0;
OSStatus result = noErr;
// Read from the URL and convert into a CFData chunk
status = CFURLCreateDataAndPropertiesFromResource (
kCFAllocatorDefault,
(__bridge CFURLRef) presetURL,
&propertyResourceData,
NULL,
NULL,
&errorCode
);
NSAssert (status == YES && propertyResourceData != 0, #"Unable to create data and properties from a preset. Error code: %d '%.4s'", (int) errorCode, (const char *)&errorCode);
// Convert the data object into a property list
CFPropertyListRef presetPropertyList = 0;
CFPropertyListFormat dataFormat = 0;
CFErrorRef errorRef = 0;
presetPropertyList = CFPropertyListCreateWithData (
kCFAllocatorDefault,
propertyResourceData,
kCFPropertyListImmutable,
&dataFormat,
&errorRef
);
// Set the class info property for the Sampler unit using the property list as the value.
if (presetPropertyList != 0) {
result = AudioUnitSetProperty(
self.samplerUnit,
kAudioUnitProperty_ClassInfo,
kAudioUnitScope_Global,
0,
&presetPropertyList,
sizeof(CFPropertyListRef)
);
CFRelease(presetPropertyList);
}
if (errorRef) CFRelease(errorRef);
CFRelease (propertyResourceData);
return result;
}
// Set up the audio session for this app.
- (BOOL) setupAudioSession {
AVAudioSession *mySession = [AVAudioSession sharedInstance];
// Specify that this object is the delegate of the audio session, so that
// this object's endInterruption method will be invoked when needed.
[mySession setDelegate: self];
// Assign the Playback category to the audio session. This category supports
// audio output with the Ring/Silent switch in the Silent position.
NSError *audioSessionError = nil;
[mySession setCategory: AVAudioSessionCategoryPlayback error: &audioSessionError];
if (audioSessionError != nil) {NSLog (#"Error setting audio session category."); return NO;}
// Request a desired hardware sample rate.
self.graphSampleRate = 44100.0; // Hertz
[mySession setPreferredHardwareSampleRate: self.graphSampleRate error: &audioSessionError];
if (audioSessionError != nil) {NSLog (#"Error setting preferred hardware sample rate."); return NO;}
// Activate the audio session
[mySession setActive: YES error: &audioSessionError];
if (audioSessionError != nil) {NSLog (#"Error activating the audio session."); return NO;}
// Obtain the actual hardware sample rate and store it for later use in the audio processing graph.
self.graphSampleRate = [mySession currentHardwareSampleRate];
return YES;
}
#pragma mark -
#pragma mark Audio control
// Play the low note
- (IBAction) startPlayLowNote:(id)sender {
UInt32 noteNum = kLowNote;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the low note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
// Stop the low note
- (IBAction) stopPlayLowNote:(id)sender {
UInt32 noteNum = kLowNote;
UInt32 noteCommand = kMIDIMessage_NoteOff << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, 0, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to stop playing the low note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
// Play the mid note
- (IBAction) startPlayMidNote:(id)sender {
UInt32 noteNum = kMidNote;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the mid note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
// Stop the mid note
- (IBAction) stopPlayMidNote:(id)sender {
UInt32 noteNum = kMidNote;
UInt32 noteCommand = kMIDIMessage_NoteOff << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, 0, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to stop playing the mid note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
// Play the high note
- (IBAction) startPlayHighNote:(id)sender {
UInt32 noteNum = kHighNote;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the high note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
// Stop the high note
- (IBAction)stopPlayHighNote:(id)sender {
UInt32 noteNum = kHighNote;
UInt32 noteCommand = kMIDIMessage_NoteOff << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent(self.samplerUnit, noteCommand, noteNum, 0, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to stop playing the high note. Error code: %d '%.4s'", (int) result, (const char *)&result);
}
// Stop the audio processing graph
- (void) stopAudioProcessingGraph {
OSStatus result = noErr;
if (self.processingGraph) result = AUGraphStop(self.processingGraph);
NSAssert (result == noErr, #"Unable to stop the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
}
// Restart the audio processing graph
- (void) restartAudioProcessingGraph {
OSStatus result = noErr;
if (self.processingGraph) result = AUGraphStart (self.processingGraph);
NSAssert (result == noErr, #"Unable to restart the audio processing graph. Error code: %d '%.4s'", (int) result, (const char *)&result);
}
#pragma mark -
#pragma mark Audio session delegate methods
// Respond to an audio interruption, such as a phone call or a Clock alarm.
- (void) beginInterruption {
// Stop any notes that are currently playing.
[self stopPlayLowNote: self];
[self stopPlayMidNote: self];
[self stopPlayHighNote: self];
// Interruptions do not put an AUGraph object into a "stopped" state, so
// do that here.
[self stopAudioProcessingGraph];
}
// Respond to the ending of an audio interruption.
- (void) endInterruptionWithFlags: (NSUInteger) flags {
NSError *endInterruptionError = nil;
[[AVAudioSession sharedInstance] setActive: YES
error: &endInterruptionError];
if (endInterruptionError != nil) {
NSLog (#"Unable to reactivate the audio session.");
return;
}
if (flags & AVAudioSessionInterruptionFlags_ShouldResume) {
/*
In a shipping application, check here to see if the hardware sample rate changed from
its previous value by comparing it to graphSampleRate. If it did change, reconfigure
the ioInputStreamFormat struct to use the new sample rate, and set the new stream
format on the two audio units. (On the mixer, you just need to change the sample rate).
Then call AUGraphUpdate on the graph before starting it.
*/
[self restartAudioProcessingGraph];
}
}
#pragma mark - Application state management
// The audio processing graph should not run when the screen is locked or when the app has
// transitioned to the background, because there can be no user interaction in those states.
// (Leaving the graph running with the screen locked wastes a significant amount of energy.)
//
// Responding to these UIApplication notifications allows this class to stop and restart the
// graph as appropriate.
- (void) registerForUIApplicationNotifications {
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver: self
selector: #selector (handleResigningActive:)
name: UIApplicationWillResignActiveNotification
object: [UIApplication sharedApplication]];
[notificationCenter addObserver: self
selector: #selector (handleBecomingActive:)
name: UIApplicationDidBecomeActiveNotification
object: [UIApplication sharedApplication]];
}
- (void) handleResigningActive: (id) notification {
[self stopPlayLowNote: self];
[self stopPlayMidNote: self];
[self stopPlayHighNote: self];
[self stopAudioProcessingGraph];
}
- (void) handleBecomingActive: (id) notification {
[self restartAudioProcessingGraph];
}
- (id) initWithNibName: (NSString *) nibNameOrNil bundle: (NSBundle *) nibBundleOrNil {
self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];
// If object initialization fails, return immediately.
if (!self) {
return nil;
}
// Set up the audio session for this app, in the process obtaining the
// hardware sample rate for use in the audio processing graph.
BOOL audioSessionActivated = [self setupAudioSession];
NSAssert (audioSessionActivated == YES, #"Unable to set up audio session.");
// Create the audio processing graph; place references to the graph and to the Sampler unit
// into the processingGraph and samplerUnit instance variables.
[self createAUGraph];
[self configureAndStartAudioProcessingGraph: self.processingGraph];
return self;
}
- (void) viewDidLoad {
[super viewDidLoad];
// Load the Trombone preset so the app is ready to play upon launch.
[self loadPresetOne: self];
[self registerForUIApplicationNotifications];
}
- (void) viewDidUnload {
self.currentPresetLabel = nil;
self.presetOneButton = nil;
self.presetTwoButton = nil;
self.lowNoteButton = nil;
self.midNoteButton = nil;
self.highNoteButton = nil;
[super viewDidUnload];
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void) didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#end
An NSTimer, in any thread, is unsuitable for precise musical timing, or anything where the app needs a timing accuracy better than within 50 milliseconds.
It's likely that your high priority timer thread is interfering with the core audio thread on these older devices. Try moving to a lower priority and reducing the time interval of your timer to test this theory. This would be a bug in Apple's code – the audio thread is the highest priority thread on iOS, and the os is supposed to insure that no user code interrupts it long enough to cause stutters, but it does happen. That said, you probably shouldn't be using a timer like this to trigger audio for most kinds of musical applications, but should rather use the timestamps core audio provides in the render callback (see RemoteIO) to handle timing. Here's a pretty good discussion:
http://atastypixel.com/blog/experiments-with-precise-timing-in-ios/
"Also note that there are often ways to eliminate the need for precise timing of this nature, by architecting code appropriately — when it comes to audio, for example, CoreAudio provides a very accurate time base in render callbacks. For things like metronomes or audio synthesizers, it’s always better to establish a starting time, and use the difference between the current time and the starting time in order to determine state, rather than using a timer to advance the state."

How to send a string in a UDP socket in iOS7?

I am trying to send a simple string over UDP in my iOS7 app to a known IP and could not find a simple explanation and sample code on how to do that.
There is plenty out there about TCP but not so much about UDP and it has to be UDP in my case.
You could use https://github.com/robbiehanson/CocoaAsyncSocket, which is an Objective-C wrapper for TCP and UDP connections. It also contains sample code for TCP and UPD clients and servers.
You could use this wrapper-less simple gist.
UDPEchoClient.h
#import <Foundation/Foundation.h>
#interface UDPEchoClient : NSObject
- (BOOL) sendData:(const char *)msg;
#end
UDPEchoClient.m
#import "UDPEchoClient.h"
//
// CFSocket imports
//
#import <CoreFoundation/CoreFoundation.h>
#import <sys/socket.h>
#import <arpa/inet.h>
#import <netinet/in.h>
#define IP "host ip"
#define PORT host_port
static void dataAvailableCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
//
// receiving information sent back from the echo server
//
CFDataRef dataRef = (CFDataRef)data;
NSLog(#"data recieved (%s) ", CFDataGetBytePtr(dataRef));
}
#implementation UDPEchoClient
{
//
// socket for communication
//
CFSocketRef cfsocketout;
//
// address object to store the host details
//
struct sockaddr_in addr;
}
- (instancetype)init
{
self = [super init];
if (self) {
//
// instantiating the CFSocketRef
//
cfsocketout = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
kCFSocketDataCallBack,
dataAvailableCallback,
NULL);
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(IP);
//
// set runloop for data reciever
//
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfsocketout, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
CFRelease(rls);
}
return self;
}
//
// returns true upon successfull sending
//
- (BOOL) sendData:(const char *)msg
{
//
// checking, is my socket is valid
//
if(cfsocketout)
{
//
// making the data from the address
//
CFDataRef addr_data = CFDataCreate(NULL, (const UInt8*)&addr, sizeof(addr));
//
// making the data from the message
//
CFDataRef msg_data = CFDataCreate(NULL, (const UInt8*)msg, strlen(msg));
//
// actually sending the data & catch the status
//
CFSocketError socketErr = CFSocketSendData(cfsocketout,
addr_data,
msg_data,
0);
//
// return true/false upon return value of the send function
//
return (socketErr == kCFSocketSuccess);
}
else
{
NSLog(#"socket reference is null");
return false;
}
}

NSNetServiceBrowser does not find Service

I tried to write a Client(iPad)/Server(iMac) application based on the CocoaEcho example. My first simple example worked, but after adding more functionality the client is unable to find the server.
After starting the server, I start the client, both in a local network. The client starts searching for services and gets a "netServiceBrowserWillSearch:" message for its browser, but after that nothing happens. Triggering the search for services again, results in a "didNotsearch:" message with error -72003, 10 (browser is still busy searching).
1) I checked that the server is reachable with the WiTap app. There client and server connect correctly.
2) I checked if the server publishes the service with "dns-sd -B _cocoaecho", it is detected.
3) The nsnetservicebrowser object in the client app is declared a property, so there should not be a scope problem. I also checked in the debugger, it is still there....
My Code:
Client:
#interface MySocketClient : UIResponder <NSNetServiceBrowserDelegate, NSStreamDelegate>
{
...
NSNetService * myServer;
NSString* nextMsg;
}
#property (nonatomic, strong, readwrite) NSMutableArray * services; // of NSNetService
#property (nonatomic, strong, readwrite) NSNetServiceBrowser * serviceBrowser;
#property (nonatomic, strong, readwrite) NSInputStream * inputStream;
#property (nonatomic, strong, readwrite) NSOutputStream * outputStream;
#property (nonatomic, strong, readwrite) NSMutableData * inputBuffer;
#property (nonatomic, strong, readwrite) NSMutableData * outputBuffer;
....
-(void) setup{
...
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
self.services = [[NSMutableArray alloc] init];
[self.serviceBrowser setDelegate:self];
[self.serviceBrowser searchForServicesOfType:#"_cocoaecho._tcp." inDomain:#"local."];
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)netServiceBrowser didNotSearch:(NSDictionary *)errorInfo
{
NSLog(#"%#", errorInfo);
}
// Sent when browsing begins
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
{
NSLog(#"will search \n");
}
// Sent when browsing stops
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
{
NSLog(#"stopped search \n");
}
//We broadcast the willChangeValueForKey: and didChangeValueForKey: for the NSTableView binding to work.
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
#pragma unused(aNetServiceBrowser)
#pragma unused(moreComing)
NSLog(#"found a service \n");
if (![self.services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[self.services addObject:aNetService];
[self didChangeValueForKey:#"services"];
myServer = aNetService;
}
}
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
#pragma unused(aNetServiceBrowser)
#pragma unused(moreComing)
if ([self.services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[self.services removeObject:aNetService];
[self didChangeValueForKey:#"services"];
}
}
And the Server:
- (BOOL)start {
assert(_ipv4socket == NULL && _ipv6socket == NULL); // don't call -start twice!
CFSocketContext socketCtxt = {0, (__bridge void *) self, NULL, NULL, NULL};
_ipv4socket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &EchoServerAcceptCallBack, &socketCtxt);
_ipv6socket = CFSocketCreate(kCFAllocatorDefault, AF_INET6, SOCK_STREAM, 0, kCFSocketAcceptCallBack, &EchoServerAcceptCallBack, &socketCtxt);
if (NULL == _ipv4socket || NULL == _ipv6socket) {
[self stop];
return NO;
}
static const int yes = 1;
(void) setsockopt(CFSocketGetNative(_ipv4socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
(void) setsockopt(CFSocketGetNative(_ipv6socket), SOL_SOCKET, SO_REUSEADDR, (const void *) &yes, sizeof(yes));
// Set up the IPv4 listening socket; port is 0, which will cause the kernel to choose a port for us.
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(0);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
if (kCFSocketSuccess != CFSocketSetAddress(_ipv4socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr4 length:sizeof(addr4)])) {
[self stop];
return NO;
}
// Now that the IPv4 binding was successful, we get the port number
// -- we will need it for the IPv6 listening socket and for the NSNetService.
NSData *addr = (__bridge_transfer NSData *)CFSocketCopyAddress(_ipv4socket);
assert([addr length] == sizeof(struct sockaddr_in));
self.port = ntohs(((const struct sockaddr_in *)[addr bytes])->sin_port);
// Set up the IPv6 listening socket.
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(self.port);
memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
if (kCFSocketSuccess != CFSocketSetAddress(_ipv6socket, (__bridge CFDataRef) [NSData dataWithBytes:&addr6 length:sizeof(addr6)])) {
[self stop];
return NO;
}
// Set up the run loop sources for the sockets.
CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv4socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source4, kCFRunLoopCommonModes);
CFRelease(source4);
CFRunLoopSourceRef source6 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _ipv6socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source6, kCFRunLoopCommonModes);
CFRelease(source6);
assert(self.port > 0 && self.port < 65536);
self.netService = [[NSNetService alloc] initWithDomain:#"local." type:#"_cocoaecho._tcp." name:#"" port:(int) self.port];
[self.netService publishWithOptions:0];
return YES;
}
I was just getting that -72003 error all the time unless I disconnected and reconnected again (even the first time). Which lead to this solution:
private let serviceBrowser = NSNetServiceBrowser()
serviceBrowser.stop()
serviceBrowser.searchForServicesOfType(TYPE, inDomain: DOMAIN)
I don't know why this works but I'm no longer getting the error.
I had similar problem. My code successfully registered NSNetService and launched NSNetServiceBrowser but could not -resolveWithTimeout other devices. Strange, but sometimes did work, sometimes not and sometimes worked asymmetrically.
After intense debugging I can give you some tips to check:
Install Bonjour Browser on desktop. Plug out your network cable and check if you are connected to the same WiFi hotspot as mobile devices. Here you should see the same service state as mobile devices will do.
Try with different WiFi hotspot. Strange but my main WiFi performed badly. After I switched to another one it worked like a charm using the very same code. Try unplug WiFi from Internet for testing.
You can add some retains (or assign to static variable) to objects returned from API (like NSNetService). ARC can do silently dealloc if it decides object is not needed anymore. That helped my for some tests.

Simple posix server can't set UILabel from loop

I'm trying to add a posix socket server to my iOS app that will allow a TCP connection and writes the buffer to a UILabel object as a test.
I can get it to work...once. Then it finishes and closes the connection. Ok, easy fix, I'll just put it in a loop. Now whenever I put the exact same code in a loop, it won't update the UILabel for some reason. I don't actually need it to be able to update the UILabel, it was just a test to make sure the server was working.......but it's making me nervous. I take it out of the while loop, it works, I put it back in, and everything but the UILabel setText call work.
Also, two other small questions: I'm having trouble figuring out how to exit the loop after a client disconnects, and I'm not sure how to correctly close the ports when I exit, I have to keep changing the port number because it can't bind.
-(void)viewDidLoad
NSThread *listenThread = [[NSThread alloc] initWithTarget:self selector:#selector(createPosixServer) object:nil];
[listenThread start];
-(void)createPosixServer
//declarations
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
NSString *nsbuffer;
//bind and listen on socket
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
NSLog(#"Error while calling socket()");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 1818;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
NSLog(#"ERROR on binding");
}
listen(sockfd, 5);
NSLog(#"Begin listen loop");
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
NSLog(#"ERROR on accept");
}
while(true) {
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) {
NSLog(#"ERROR reading from socket");
}
if (n > 0) {
nsbuffer = [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
[_lblStatus setText:nsbuffer];
NSLog(#"You sent %#", nsbuffer);
}
NSLog(#"Finished listen loop");
sleep(1);
}
close(newsockfd);
close(sockfd);
NSLog(#"Socket closed");
createPosixServer is running on a background thread. It is never safe to update the UI from a background thread. UIKit will sometimes work, sometimes just ignore you. You need to dispatch the call to update the label onto the main thread, something like this:
typeof(self) __weak weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.lblStatus.text = nsbuffer;
});
To answer the side question: when a client disconnects you want to close the socket that you accepted for that connection (your newsockfd) but you don't want to close your listener sockfd until you are tearing down the whole service.
To exit the loop, simply do this:
if (n < 0) {
NSLog(#"ERROR reading from socket");
break;
}
Though you probably want to check errno in that block too because you're probably going to want different behavior depending on the error.
Remove the sleep(1), that is doing nothing good for you. The read call will block, there's no need to sleep.

Resources