performSelectorOnMainThread method not call - objective-c

I have created a method that is running in new thread.
[NSThread detachNewThreadSelector:#selector(setmostpopularReq:) toTarget:self withObject:mostPopulerstring];
After completed this method i send all data to main thread.
[self performSelectorOnMainThread:#selector(getmostpopularResponse:) withObject:self waitUntilDone:YES];
But some time my main thread method not calling.
i used
dispatch_sync(dispatch_get_main_queue(),^{[self getmostpopularResponse:mostPopularList];});
But this is also have the same problem some time its calling method or some time not calling.
Please help me in this.

I would advise you to create a delegate with which you could notify the main thread after the
completion of the detached thread
Also another solution would be to create an NSOperation and NSOperationQueue instead of a new thread. There you can schedule what you want. For me looks easier, though it depends on you.
Here is a link to help you more with NSOperation
https://developer.apple.com/library/mac/#featuredarticles/ManagingConcurrency/_index.html

I will write this really quickly.
#protocol RespondDelegate
- (void)notifyWithRespond:(NSData *)data;
#end
#interface ContactWebServiceOperation:NSOperation
#property (nonatomic, assign) id delegate;
#end
#implementation ContactWebServiceOperation
#synthesize delegate;
// initialize here.
- (id)initWithDelegate:(id)delegate;
{
if ([self = [super init]) {
self.delegate = delegate;
}
return self;
}
- (void)main
{
if (self.isCancelled) return;
if (nil != delegate) {
// Do your work here...
work();
// When finished notify the delegate with the new data.
[delegate notifyWithRespond:your_data_here];
// Or
[delegate performSelectorOnMainThread:#selector(processImageForDownloadOperation:)
withObject:self waitUntilDone:YES];
}
}
#end
// Now on the view that you want to present the received results
// you have to do one thing.
// Let's say that your view is called View1
#interface View1 : UIViewController<RespondDelegate>
// Here put whatever you like.
#end
#implementation View1
// Put here all your code.
- (void)notifyWithRespond:(NSData *)data
{
// Here you will handle your new data and you will update your view.
}
#end
If I understand correct this should work.
Also, you can change the NSData to whatever you like, as long as you perform the appropriate conversions later.
If it doesn't work take a look on the link from Apple, maybe I have some typo or something.
But in general it looks solid.

Related

Objective-C Shared variables between classes

Basically I've implemented a connection method which parses a JSON from an URL, via sendAsynchronousRequest. Everything's working nicely. But at the end of the sendAsynchronousRequest function, I need to reload a tableView (since the data arrived and I need to show it).
Currently I'm doing it by sending the tableView as parameter to the function of the class that does the connection
#implementation WhosWhereConnection
- (void)setUpConnection:(UITableView *)tableView {
...
[tableView reloadData];
...
}
And calling the function with
[connection setUpConnection:self.tableView];
It's working as I intended, but I feel this ins't the most elegant way of doing it. What would you recommend?
Wish I could accept all your answers, thank you for helping :)
I would recommend using blocks for this. It is convenient and very strong solution.
Something like this:
Method header (.h file)
- (void)setupConnectionWithCompletion:(void(^)())completionBlock;
Method implementation (.m file)
- (void)setupConnectionWithCompletion:(void(^)())completionBlock
{
// Do your stuff
// Call completion block (if set) when everything is done
if(completionBlock) {
completionBlock();
}
}
And call it like this
[connection setupConnectionWithCompletion:^{
[tableView reloadData];
}];
Better to have a delegate method / block which is called on completion, or to post a notification (if multiple instances are interested in the event). This will allow you to break the dependency you currently have by making the actions performed as a result of the completion event anonymous to the WhosWhereConnection class. The simplest change will be to replace the table view parameter with a block.
Using a delegate requires the most code. The other answers show the code for the other options.
For delegation, we want:
A protocol to define method(s) that will be called
A property to hold the delegate object reference
Usage of the delegate
Implementation of the delegate method(s)
1, 2 & 3 are on WhosWhereConnectionDelegate class. 4 is on the table view controller.
1.
#protocol WhosWhereConnectionDelegate < NSObject >
- (void)connection:(WhosWhereConnectionDelegate *)connection didCompleteWithStatus:(BOOL)status;
#end
2.
#property (weak, nonatomic) id < WhosWhereConnectionDelegate > delegate;
3.
You don't show what setUpConnection does, but the delegate call should be made once the connection is complete.
- (void)setUpConnection {
BOOL status = NO;
...
// stuff here to process things and determine the status
...
[self.delegate connection:self didCompleteWithStatus:status];
...
}
4.
The table view controller sets itself as the delegate of the connection before the connection starts.
- (void)connection:(WhosWhereConnectionDelegate *)connection didCompleteWithStatus:(BOOL)status
{
[self.tableView reloadData];
}
NSNotifications is what you need.
- (void)setUpConnection{
//...
[[NSNotificationCenter defaultCenter] postNotificationName:#"notificationName" object:yourData];
//...
}
In your viewController:
- (void)viewDidLoad
{
//...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataDidLoad:) name:#"notificationName" object:nil];
//...
}
- (void)dataDidLoad:(NSNotification*)notification
{
//do your stuff
[tebleView reloadData];
}

Objective-C methods not running

I'm messing around with using objects to launch background threads, however when I call an objects method to call the method that will spawn a background thread, nothing happens. I'm a bit puzzled as to why, and it looks like the -init function isn't even being called. Anyways, here's what I have:
ViewController.h
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
#property(nonatomic, strong) Thread* threadedObject;
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "Threader.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_threadedObject = [[Threader alloc]init];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender {
NSLog(#"Clicked.");
[_threadedObject RunInBackground];
}
#end
Threader.h
#import <Foundation/Foundation.h>
#interface Threader : NSObject
#property(nonatomic) bool IsFinishedRunning;
#property(nonatomic) bool IsThreading;
//Constructor and Destructor
-(id)init;
-(void)dealloc;
-(void)RunInBackground;
-(void)WaitForTenSeconds;
#end
Threader.m
#import "Threader.h"
#implementation Threader
//constructor
-(id)init{
[super init];
if(self != nil)
{
_IsFinishedRunning = NO;
_IsThreading = NO;
}
return self;
}
//destructor
-(void)dealloc{
[super dealloc];
}
//Runs a thread in the background
-(void)RunInBackground{
NSLog(#"Initiating thread...");
[self performSelectorInBackground:#selector(WaitForTenSeconds) withObject:nil];
}
//Waits for 10 seconds, then sets IsFinishedRunning to YES
-(void)WaitForTenSeconds{
NSLog(#"Starting to run in the background.");
_IsThreading = YES;
sleep(10);
_IsFinishedRunning = YES;
NSLog(#"Finished running in the background.");
}
#end
When I run the program, this is my output(I clicked the button a few times)
2013-05-17 15:30:57.267 ThreadedObjects Clicked.
2013-05-17 15:30:59.003 ThreadedObjects Clicked.
2013-05-17 15:30:59.259 ThreadedObjects Clicked.
2013-05-17 15:30:59.443 ThreadedObjects Clicked.
2013-05-17 15:30:59.675 ThreadedObjects Clicked.
I should be getting messages telling me that the Threader object was created, and that it is preparing to launch a background thread, that the thread has been spawned and then after 10 seconds, that the thread is done running.
So, where's my glaring obvious error?
init isn't a constructor, it's for setup after construction. You need the class object to create an instance before you can send init, and, most importantly, you need to assign the results to your variable.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
threadedObject = [[Threader alloc] init];
}
You can't send alloc to an object that's not a class; instances don't respond to it. The only reason that this isn't crashing is that globals are initialized to 0/NULL/nil, and [nil someMessage] does nothing.
Not assigning the results to your variable is the same as:
int x = 0;
x + 10;
There's no change to x's value.
Additionally, you don't seem to have an ivar there, just a global variable. Ivars need to go into a curly-brace block at the head of the #implementation:
#implementation Threader
{
Threader * threadedObject;
}
// etc...
You never alloc the object.............
Also, this is curious:
#import <UIKit/UIKit.h>
#import "Threader.h"
#interface ViewController : UIViewController
- (IBAction)StartBackgroundThreadButtonClicked:(id)sender;
#end
Threader* threadedObject;
Where exactly did you declare the threadedObject? Like above? Use an iVar
or, better, a property for it!
A couple of reactions:
Show us where your definition and alloc/init of threadedObject.
I'm not sure what business problem you're trying to solve, but this smells like the precursor of some custom NSOperation solution. Operation queues are ideally suited for these sorts of implementations.
I'd be inclined to subclass NSOperation when trying to do something like this. See the custom NSOperation object in the Concurrency Programming Guide.
I'd suggest using camelCase for your method and variable names.
If you say with this, I'd steer you away from the "thread" name, as it might imply that you're doing something with NSThread, which you're not.

Objective-C iOS 6 delegate NSString query

I'm trying to use a delegate to pass a value from one VC to another. I think I'm am misunderstanding the way it is supposed to work.
In my main ViewController.h I have this:
#protocol defaultLocationChoice <NSObject>
- (NSString *) locChoice;
#end
In both my PreferencesViewController.h and ChooseServerViewController.h I have defaultLocationChoice declared in the #interface section and the property assinged like so:
#property (nonatomic, assign) id <defaultLocationChoice> locationDelegate;
Both are synthesized also.
When the user segues from PreferencesViewController to ChooseServerViewController the prepare for segue code is:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"toServerChoice"]) {
ChooseServerViewController *viewController = (ChooseServerViewController *)segue.destinationViewController;
viewController.locationDelegate = self;
}
}
When a cell choice is made in ChooseServerViewController I call:
[self locChoice];
Which is:
- (NSString *) locChoice {
NSLog(#"Cell Vale Loc choice %#",cellValue);
return cellValue;
}
The NSLog verifies the correct value is returned.
Now, as I think I understand it, the value of LocChoice in the delegate is now the value returned, no?
When the user goes back (NavController) the PreferencesViewController has:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
defaultLocation = [locationDelegate locChoice];
[self.tableView reloadData];
}
I was expecting the value of defaultLocation to now equal the value passed to locChoice. However when the table reloads the cell in question is still blank, implying what I exepct to happen isn't happening.
Any ideas?
If I followed your code properly, you do not need to adopt the mentioned protocol in your ChooseServerViewController, only PreferencesViewController.
The reasoning is you want to send data back to the previous view controller. Try:
#protocol defaultLocationChoice <NSObject>
- (void) locChoice:(NSString*)choice;
#end
Have your PreferencesViewController implement that method so it receives the selection. You will have to store that in an appropriate instance variable.
// in PreferencesViewController.m
-(void)locChoice:(NSString*)choice {
self.choice = choice; // this just my example
}
When the choice is made (in ChooseServerViewController) to send the choice back, call:
// this is in 'ChooseServerViewController.m' some where appropriate
[self.delegate locChoice:cellValue];
Your implementation is simply doing nothing with cell value (not even storing it, just logging it). When you return to PreferencesViewController, you will now have the selected value and that view controller can what it wants with it.
Protocols are somewhat analgous to interfaces in Java or C#, but more flexible.
Some more concepts about delegation.
Working with Protocols.
UPDATE:
The declaration for ChooseServerViewController should look like:
#import "FileWithProtocolDecalration.h"
#interface ChooseServerViewController
.
.
.
#property ( nonatomic,assign) id<defaultLocationChoice> delegate;
.
.
.
#end
I think you do have some misunderstanding there
protocol and delegates are something that is passed around. in other words somebody need to be receiver and somebody need to be the sender. in your case.
update your protocol to this
#protocol defaultLocationChoice <NSObject>
- (void)locChoice:(NSString *)updateString; // the method from delegate and implementer must be exact
#end
and set implement the protocol to ViewController as a receiver
#interface VCReceiver : UIViewController <defaultLocationChoice>
then later in VCReceiver
- (void)viewDidLoad {
ChooseServerViewController *vcSender = [[ChooseServerViewController alloc] init];
[vcSender setLocationDelegate:self]; // this is like saying. i have implemented method from protocol in this file (self)
}
- (void)locChoice:(NSString *)updateString {
// update the VCReceiver here
// or access vcSender value
// or use the updateString value
}
then in ChooseServerViewController locChoice: method (the one from your example) replace with this one and call [self updateChoice] instead:
- (void)updateChoice {
if ([self.locationDelegate respondsToSelector:#selector(locChoice:)]) {
[self.locationDelegate locChoice:aStringToUpdate]; // this will call VCReceiver locChoice
}
it does not have to return anything because it is actually calling the VCReceiver method to tell it that ChooseServerViewController got the value ready to be read.

creating instances in objective c

Here is my code:
//ECHOAppDelegate.m
#implementation ECHOAppDelegate
...
#end
//PtyView.m
#interface PtyView (PtyPrivate)
-(void)startTask;
-(void) didRead: (NSNotification *)fileNoty;
#end
#implementation PtyView
...
-(void)startTask {
//starts task
}
#end
Now, how do I trigger "startTask" from ECHOAppDelegate.m? I need to create an instance? I'm a total beginner :D
Any example code would be awesome!
Thanks,
Elijah
-(void)startTask; appears to be private implementation and in theory should not be called from external classes.
To answer your question, you can call it something like this:
PtyView *v = [[PtyView alloc] init];
[v startTask];
[v release];
Though you will get a warning saying, PtyView might not respond to startTask. Since it is not in public interface of class.
Update: Above code assumes that when startTask returns, you are done with this object. But something tells me that you might be using async callbacks. If that is the case then startTask might return immediately and you won't release it then and there. Normally in this case, you will be notified by PtyView about the completion of task. So you release it when the task is complete.
Update2:
Making a method public is easy. You just declare it in the public interface (the header file of class):
//in PtyView.h
#interface PtyView
-(void)startTask;
#end
//in PtyView.m
#implementation PtyView
...
-(void)startTask {
//starts task
}
#end
Notice that there is no category defined in the interface declaration.
self represent the current object.
You just need to call the method like that.
[self startTask];
How about subclassing ECHOAppDelegate? (Then make sure PtyView inherits from NSObject?)
// cf. http://amath.colorado.edu/pub/mac/programs/PseudoTTY.zip
#interface ECHOAppDelegate : PtyView
...
#end
#implementation ECHOAppDelegate
- (id) init
{
self = [super init];
if (!self) return nil;
...
return self;
}
...
[self startTask];
...
#end

Cocoa: Calling a method from AppDelegate.m

In order to better understand the startup, event queue, and methods within my application I'm trying to write a program that does two things: Play a beep at the startup and every time the user hits a button. So far it only plays when the user hits the button. I know there may be multiple ways to get the startup beep to play, but in order to work with initialization code I want to do it by calling my beep method from within the applicationDidFinishLaunching method of the AppDelegate.m file.
Here is my code:
Log.h
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m
#import "Log.h"
#implementation Log
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[self beepAndLog];
}
#end
And the applicationDidFinishLaunching method looks like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[Log beepAndLog];
}
In the applicationDidFinishLaunching method, however, XCode warns me that
'Log' may not respond to '+beepAndLog'
and indeed, there is no beep and the log reads as follows:
MethodResponse[11401:a0f] +[Log
beepAndLog]: unrecognized selector
sent to class 0x100002100
("MethodResponse" is the name of my project, btw)
I'm unsure why Log wouldn't respond to beepAndLog, seeing as that's one of its methods. Am I calling it incorrectly? I have a feeling this will be painfully obvious to you more experienced people. I'm a newbie. Any help would be appreciated! Thanks!
There are two possibilities. Either you defined beepAndLog as an instance method, when you wanted a class method, or you want to call it on an instance when you called it on the class.
To change it to a class method, change the header to read:
+(void)beepAndLog;
and the implementation:
+(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
For the other solution, make sure you have an instance of class Log around (probably a singleton), and do something like:
[[Log logInstance] beepAndLog];
from your notification method. The Log class would need to look something like this:
Log.h:
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
+(Log *)logInstance;
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m:
#import "Log.h"
Log *theLog = nil;
#implementation Log
+(Log *)logInstance
{
if (!theLog) {
theLog = [[Log alloc] init];
// other setup (like hooking up that IBAction)
}
return theLog;
}
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[[Log logInstance] beepAndLog];
}