I have 2 classes as NSObject subclasses:
1st class is more likely act as adapter. It send data to Class2 for process async task. When delegaton fired I would like to post back data to adaptor class.
In adaptor class:
Class2 *cls = [[Class2 alloc] init];
[ftc fetchLocation];
In Class2.m
-(void)fetchLocation{
if(IS_OS_8_OR_LATER) {
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager = [[CLLocationManager alloc] init];
if ([CLLocationManager locationServicesEnabled]){
NSLog(#"Enable");
}
self.locationManager.delegate =self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager requestWhenInUseAuthorization];
[self.locationManager requestAlwaysAuthorization];
[self.locationManager startUpdatingLocation];
}
When I call fetch-location method from adaptor, it really calls and reads lines, but after that, Class2 disappears and gone back to Adapter class without waiting delegation (didUpdateLocations)
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSString *latitude = [NSString stringWithFormat:#"%f",self.locationManager.location.coordinate.latitude];
NSString *longtitude = [NSString stringWithFormat:#"%f",self.locationManager.location.coordinate.longitude];
NSString *altitude = [NSString stringWithFormat:#"%f",self.locationManager.location.altitude];
NSString *speed = [NSString stringWithFormat:#"%f",self.locationManager.location.speed];
NSDictionary *locationDictionary = #{#"latitude":latitude,#"longtitude":longtitude,#"altitude":altitude,#"speed":speed};
if (locations.count >0 && [locations isKindOfClass:[NSArray class]]) {
[self.delegate userLocationHasUpdated:self :locationDictionary];
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
return;
}
}
But if I just run Class2 and remove adapter from compile (with first initialiser) it runs as expected, How can I achieve to handle delegation methods from another class that fired ?
Best Regards.
You have several options, really. One could be making your second class a property of your first class (singleton pattern would fit nice here, I guess). Then you can either declare a protocol in your second class and notify your first class via delegate methods (non-singleton implementation) or use NSNotificationCenter to post a notification (singleton implementation).
The second option would be to pass a block with completion handler to the second class. You could declare your method in the second class like this, for example (adjust return type and arguments of the block, if needed):
- (void)updateLocationWithCompletionHandler:(void (^)(void))completion;
Implement it so that you call the completion block after you get the geolocation update results.
And call it from the first class like:
[self.secondClass updateLocationWithCompletionHandler:^
{
// Your completion code here.
}];
Hope this helps (sorry, didn't check the code in Xcode, get rid of typos, if any).
Possible duplicate of iOS CLLocationManager in a separate class. You have to create a singleton class for to get the location if you want to have seperate class for handing the location manager. You will find the guidance from the shared link
Related
Q1: Can I call a method and have it execute on a background thread from inside another method that is currently executing on the main thread?
Q2: As an extension of the above, can I call a method and have it execute on a background thread from inside another method that is currently executing on some other background thread itself?
Q3: And one final question given the above : if I initialize an instance of some object X on some thread (main/background) and then have a method Y, of that object X, executing on some other background thread, can this method Y send messages and update an int property (e.g. of that Object X, or is such communication not possible ?
The reason I'm asking this last question is because I've been going over and over it again and I can't figure what is wrong here:
The following code returns zero acceleration and zero degrees values :
MotionHandler.m
#implementation MotionHandler
#synthesize currentAccelerationOnYaxis; // this is a double
-(void)startCompassUpdates
{
locationManager=[[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.delegate=self;
[locationManager startUpdatingHeading];
NSLog(#"compass updates initialized");
}
-(int) currentDegrees
{
return (int)locationManager.heading.magneticHeading;
}
-(void) startAccelerationUpdates
{
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
self.currentAccelerationOnYaxis = motion.userAcceleration.y;
}
];
}
#end
Tester.m
#implementation Tester
-(void)test
{
MotionHandler *currentMotionHandler = [[MotionHandler alloc] init];
[currentMotionHandler performSelectorInBackground:#selector(startCompassUpdates) withObject:nil];
[currentMotionHandler performSelectorInBackground:#selector(startAccelerationUpdates) withObject:nil];
while(1==1)
{
NSLog(#"current acceleration is %f", currentMotionHandler.currentAccelerationOnYaxis);
NSLog(#"current degrees are %i", [currentMotionHandler currentDegrees]);
}
SomeViewController.m
#implementation SomeViewController
-(void) viewDidLoad
{
[myTester performSelectorInBackground:#selector(test) withObject:nil];
}
#end
However, the following code returns those values normally :
Tester.m
#interface Tester()
{
CLLocationManager *locationManager;
double accelerationOnYaxis;
// more code..
}
#end
#implementation Tester
- (id) init
{
locationManager=[[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.delegate=self;
[locationManager startUpdatingHeading];
// more code..
}
-(void) test
{
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]
withHandler:^(CMDeviceMotion *motion, NSError *error)
{
accelerationOnYaxis = motion.userAcceleration.y;
}
];
while(1==1)
{
NSLog(#"current acceleration is %f", accelerationOnYaxis);
NSLog(#"current degrees are %i", locationManager.heading.magneticHeading);
}
}
SomeViewController.m
#implementation SomeViewController
-(void) viewDidLoad
{
[myTester performSelectorInBackground:#selector(test) withObject:nil];
}
What's wrong with the first version? I really want to use that first one because it seems much better design-wise.. Thank you for any help!
Calling performSelectorInBackground:withObject: is the same as if you called the detachNewThreadSelector:toTarget:withObject: method of NSThread with the current object, selector, and parameter object as parameters (Threading Programming Guide). No matter where you call it, a new thread will be created to perform that selector. So to answer your first two questions: yes and yes.
For your final question, as long as this Object X is the same object in both methods, any of X's properties can be updated. But, beware that this can yield unexpected results (ie. see Concurrency Programming Guide). If multiple methods are updating X's property, values can be overwritten or disregarded. But, if you are only updating it from method Y and reading it from all other methods, such problems shouldn't occur.
You should take a look at the Grand Central Dispatch documentation from Apple. It allows you to use multiple threads in a block-based structure.
2 importants function are dispatch_sync() and dispatch_async().
Some examples:
To execute a certain block of code on a background thread and wait until it is finished:
__block id someVariable = nil;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// do some heavy work in the background
someVariable = [[NSObject alloc] init];
});
NSLog(#"Variable: %#", someVariable);
This function modifies the variable someVariable which you can use later on. Please note that the main thread will be paused to wait for the background thread. If that is not what you want, you can use dispatch_async() as follows:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// do some heavy work in the background
NSObject *someVariable = [[NSObject alloc] init];
// notify main thread that the work is done
dispatch_async(dispatch_get_main_queue(), ^{
// call some function and pass someVariable to it, it will be called on the main thread
NSLog(#"Variable: %#", someVariable);
});
});
I'm still new to Objective-C so I'm having a hard time with this. In my AppController, When a user clicks on one of the ads in my app, then closes the ad and returns to my app, I would like to destroy and recreate the ad (Long story as to why). For some reason though, my code isn't working. There are no errors or warnings, but it doesn't do what I intended it to do. This is what my code looks like:
#import "MoPubManager.h"
......
- (void)applicationWillEnterForeground:(UIApplication *)application
{
MoPubManager *obj = [[MoPubManager alloc] init];
if( obj.adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[obj.adView refreshAd];
}
_adView, destroy banner, and refresh ad are both in the MoPubManager file, so as you can see I imported the file and turned MoPubManager into an object. (obj.adView was originally just _adView in MoPubManager.h, but I had to switch it to obj.adView to avoid warnings.) I may just be using the wrong calls, I would post the MoPubManager.mm file where the calls originally are but its a full page or two
In the line:
MoPubManager *obj = [[MoPubManager alloc] init];
You are creating a new instance of the MoPubManager class.
Next you are checking whether the adView property of that newly instantiated class is not nil. Unless adView gets populated in the init method of MoPubManager, this will always be nil, so the destroyBanner method will not be called.
However, the destroyBanner method is being called on a sharedManager... indicating a singleton pattern in use. Therefore you should not be creating a new instance as this goes against the reason for using a singleton. (You only ever have one instance of a class when using a singleton - see this for more info)
Without seeing more code, it seems that you should be calling something like:
if( [MoPubManager sharedManager].adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[[MoPubManager sharedManager].adView refreshAd];
First, what subclass is MoPubManager... It compiles fine? The problem here seems to be that you are creating a new MoPubManager instance, but you are using a singleton to destroy it, and they don't have the same reference. You should use something diferent like:
MoPubManager *obj = [[MoPubManager alloc] init];
if( [MoPubManager sharedManager].adView ) {
[[MoPubManager sharedManager] destroyBanner];
}
[[MoPubManager sharedManager].adView refreshAd];
You are not using correctly the singleton pattern.
try this , maybe will help you
- (void)applicationWillEnterForeground:(UIApplication *)application
{
MoPubManager *obj = [MoPubManager sharedManager];
if( obj.adView ) {
[obj destroyBanner];
}
[obj.adView refreshAd];
}
I'm running into a problem that I'm not sure how to solve. Let me just give some relevant code.
FrontpageViewController (viewDidLoad)
NewsFetcher *newsFetcher = [[NewsFetcher alloc] initWithURL:url];
newsFetcher.delegate = self;
[newsFetcher loadData];
NewsFetcher.h
#property (nonatomic, unsafe_unretained) id <NewsFetcherDelegate> delegate;
I'm using unsafe_unretained because I want my app to work with iOS 4 as well, while still using ARC for convenience.
NewsFetcher.m
- (id)initWithURL:(NSURL *)url {
self = [super init];
if (self) {
self.url = url;
self.receivedData = [[NSData alloc] init];
}
return self;
}
- (void)loadData {
NSLog(#"%#", self.delegate); // FrontpageViewController, as expected
NSURLRequest *request = [NSURLRequest requestWithURL:self.url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:15];
if (self.connectionInProgress)
[self.connectionInProgress cancel];
self.connectionInProgress = [[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:YES];
}
This all works fine. NewsFetcher conforms to the NSURLConnectionDelegate protocol, so the next method that's being called is connection:didReceiveData:. However, when I do another NSLog(#"%#", self.delegate) within that method, I get varied results (EXEC_BAD_ACCESS, NSCFDictionary, etc.). I think this means that my delegate property points to a released object, which is weird because it's supposed to be the view controller that's still on screen (and therefore couldn't have been released, right?).
How is my delegate available in one method, but not anymore in the next method? Does it have to do with the unsafe_unretained?
Delegate objects are not retained (by convention) by callers. The expectation is that the caller who set it on your object will retain it. Recommend you use the Instruments tool with zombies (and then with leaks) to see what's going on.
I am trying to test a method that instantiates an instance of MFMailComposeViewController. The method being tested calls several methods of MFMailComposeViewController including setSubject:.
I want to test that setSubject is sent a specific NSString, in this case #"Test Message".
No matter what I specify for the expected string in the mock stub there is no failure.
In the Unit Test class:
#import <OCMock/OCMock.h>
- (void)testEmail {
TestClass *testInstance = [[TestClass alloc] init];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock stub] setSubject:#"Test Message"];
[testInstance testMethod];
}
In TestClass:
- (void)testMethod {
MFMailComposeViewController *mailComposeVC = [[MFMailComposeViewController alloc] init];
[mailComposeVC setSubject:#"Bad Message"];
}
Test Suite 'Email_Tests' started at 2011-09-17 18:12:21 +0000
Test Case '-[Email_Tests testEmail]' started.
Test Case '-[Email_Tests testEmail]' passed (0.041 seconds).
The test should have failed.
I am testing this in the iOS simulator and get the same result on a device.
What am I doing wrong? Is there some way to accomplish this?
You create a mock, but never pass it to the class under test, or ask the mock to verify itself. You need some form of dependency injection to say, "Instead of using MFMailComposeViewController, use this other thing I'm giving you."
Here's one way to do that. In the class under test, instead of allocating MFMailComposeViewController directly, get it through a factory method, like this:
#interface TestClass : NSObject
- (void)testMethod;
// Factory methods
+ (id)mailComposeViewController;
#end
Here's the implementation. You were leaking, so note that the factory method returns an autoreleased object.
- (void)testMethod {
MFMailComposeViewController *mailComposeVC =
[[self class] mailComposeViewController];
[mailComposeVC setSubject:#"Bad Message"];
}
+ (id)mailComposeViewController {
return [[[MFMailComposeViewController alloc] init] autorelease];
}
Over on the testing side, we create a testing subclass that overrides the factory method so it provides whatever we want it to:
#interface TestingTestClass : TestClass
#property(nonatomic, assign) id mockMailComposeViewController;
#end
#implementation TestingTestClass
#synthesize mockMailComposeViewController;
+ (id)mailComposeViewController {
return mockMailComposeViewController;
}
#end
Now we're ready for the test. I do a few things differently:
Allocate a testing subclass rather than the actual class (and don't leak!)
Set up the mock with an expectation, not just a stub
Inject the mock into the testing subclass
Verify the mock at the end
Here's the test:
- (void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
[testInstance setMockMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
}
For completeness, we need one final test, and that's to guarantee that the factory method in the actual class returns what we expect:
- (void)testMailComposerViewControllerShouldBeCorrectType {
STAssertTrue([[TestClass mailComposeViewController]
isKindOfClass:[MFMailComposeViewController class]], nil);
}
Jon Reid's is a reasonable approach, though it seems that making mailComposeViewController a class method complicates it. And subclassing it in your test code means you'll always get the mocked version at test time, which may not be what you want. I'd make it an instance method. Then you can use a partial mock to override it at test time:
-(void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
id mockInstance = [OCMockObject partialMockForObject:testInstance];
[[[mockInstance stub] andReturn:mock] mailComposeViewController];
[testInstance testMethod];
[mock verify];
}
If you keep it as a class method, you might consider making it a static global and exposing a way to override it:
static MFMailComposeViewController *mailComposeViewController = nil;
-(id)mailComposeViewController {
if (!mailComposeViewController) {
mailComposeViewController = [[MFMailComposeViewController alloc] init];
}
return mailComposeViewController;
}
-(void)setMailComposeViewController:(MFMailComposeViewController *)controller {
mailComposeViewController = controller;
}
Then, your test would be similar to Jon's example:
-(void)testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
[testInstance setMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
// clean up
[testInstance setMailComposeViewController:nil];
}
So I'm still kind of new to Objective-C, and this was my first app that I'm now updating. The idea is this: The whole app is basically various lists of stuff. It asks the API for 15 posts, shows those with a Load More button. Click Load More, it loads 15 more, etc. The API that it loads these from has a token system with a timeout built in. Too long between requests, and you have to get a new token. So I want to have a singleton to use anywhere in my app so I can just do [APIMachine getToken] and behind the scenes, it checks if the time since the last request was too long (or this is the first request), if so, gets a new token, otherwise returns the one we already have. I'm following the singleton pattern I've found in so many places, but every time the Load More button uses [APIMachine getToken]it gets either nothing or something completely random. I had it print this stuff in the logs, and one time I even got a UITableViewCell as my token. Looks like variables are being overwritten somehow. But I really can't figure it out.
So here it is:
static PoorAPI2 *_instance;
#implementation PoorAPI2
#synthesize apiToken, timeOpened, tokenTTL;
+ (PoorAPI2*)sharedAPI
{
#synchronized(self) {
if (_instance == nil) {
_instance = [[super allocWithZone:NULL] init];
}
}
return _instance;
}
-(NSString *)API_open{
//boring code to get api token redacted
if ([doneness isEqualToString:#"success"]) {
NSDictionary *data = [json objectForKey:#"data"];
apiToken = [data objectForKey:#"api_token"];
tokenTTL = [data objectForKey:#"ttl"];
timeOpened = [NSDate date];
}else{
NSLog(#"FFFFFFFUUUUUUUUUUUU this error should be handled better.");
}
return apiToken;
}
-(BOOL)isConnectionOpen{
return ([timeOpened timeIntervalSinceNow] > tokenTTL);
}
-(NSString *)getToken{
if([self isConnectionOpen]){
return apiToken;
}else{
return [_instance API_open];
}
}
-(id)init{
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedAPI]retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#end
I can only hope I'm doing something seriously foolish and this will be a hilarious point-and-laugh-at-that-guy thread. Then at least my app will work.
In API_open, you store three objects in instance variables, but they're not objects you own, so they'll probably be gone by the time you need them and replaced by something unpredictable. You need to retain them or use proper setters.
You problem is:
static PoorAPI2 *_instance;
C, and by inheritance Objective-C, do not initialize variables. Just change to:
static PoorAPI2 *_instance = nil;
Also I am of the school that adding extra code to try to prevent the singleton from being used as a single is a total waste of time, and only give you more code with more possibilities for bugs.
So if I was you then I would remove every method from +[PoorApi2 allocWithZone:] and down. Objective-C is a dynamic language and if a client wanted to instantiate a second instance of your singleton then it would be able to do so despite all your wasted extra lines of code. At the most I would add a log like this:
-(id)init{
if (_instance) NSLog(#"WARNING: PoorAPI2 already has a shared instance.");
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
Creating a second instance of a singleton is a programming error and should be caught in development. Not a problem you should add extra lines of code to hide.