I'm looking for a solution that solves the following problem:
I have a NSOperation which download the image in the background:
#protocol CoreImageDownloadingOperationDelegate <NSObject>
#required
-(void) handleResponse:(UIImage*) response;
-(void) handleException:(MobileServiceException*) exception;
#end
#interface CoreImageDownloadingOperation : NSOperation{
}
-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;
#property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;
When it's finish the downloading, calling the delegate method, to set the image to the imageView:
pragma mark - overridden from NSOperation
- (void) main {
if (self.isCancelled)
return;
#autoreleasepool {
#try {
UIImage* image = [[CoreEnvironment sharedInstance] getImageFromServer: [imageID stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
if (self.isCancelled)
return;
if(delegate){
[delegate handleResponse:image];
}
else
NSLog(#"CachedImageView already deallocated");
}#catch (NSException *exception) {
TestLog(#"%#", exception.reason);
if (self.isCancelled)
return;
if(delegate && [exception isKindOfClass:[MobileServiceException class]])
[delegate handleException:(MobileServiceException*)exception];
}
}
}
The problem is: when I go to another page while the image is downloading, the cachedImageView is deallocated, but when the imageDownloadingOperation finishes downloading, the delegate is not nil, and it's trying to handleResponse... And of course I get message sent to deallocated...
I alloc init the operation like this in the CachedImageView:
CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
or:
-[CachedImageView isKindOfClass:]: message sent to deallocated instance 0x18868550
The problem is: when I go to another page while the image is
downloading, the cachedImageView is deallocated
The usual way to deal with this is to remove itself as a delegate in the dealloc of CachedImageView. Like
// in CachedImageView
- (void)dealloc {
// CachedImageView keeps a reference to the operation
// called imageDownloadingOperation
imageDownloadingOperation.delegate = nil;
[super dealloc];
}
Where is your protocol declaration? I'd expect to see this:
#protocol CoreImageDownloadingOperationDelegate <NSObject>
- (void) handleResponse:(UIImage *) image;
#end
#interface CoreImageDownloadingOperation : NSOperation{
}
-(id) initWithDelegate:(id<CoreImageDownloadingOperationDelegate>)del andImageID: (NSString *) image;
#property (nonatomic, assign) id <CoreImageDownloadingOperationDelegate> delegate;
You are getting the warning/crash because it can't find the responder handleResponse:
Also when invoking the delegate your better off doing:
if ([self.delegate respondsToSelector:#selector(handleResponse:)])
[self.delegate handleResponse:image];
You don't need to check if (self.delegate && [self.delegate responds .... as it will return nil if the delegate is nil && if the selector is not implemented.
EDIT *
Where you create:
CoreImageDownloadingOperation* imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
I suspect this is being released, turn this into a property of the class it's in. Then try again (make sure to release it when you're done though) i.e
In your .h
#property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;
Then initialise with:
if (!self.imageDownloadingOperation)
self.imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
A better way of writing the code is :
if([self.delegate respondsToSelector:#selector(handleResponse:)){
[self.delegate handleResponse:image];
}
This will avoid your crash.
I've solved the problem, I used CW0007007 solution and my own solution. So I turned my operation into a retained property:
#property (nonatomic, retain) CoreImageDownloadingOperation* imageDownloadingOperation;
after this I checked if the operation is still alive
if (!imageDownloadingOperation)
imageDownloadingOperation = [[CoreImageDownloadingOperation alloc] initWithDelegate:self andImageID:imageKey];
then added to the operation queue.
In the dealloc set the delegate to nil ( ofc only if the operation is alive ), and release it:
if (imageDownloadingOperation) {
imageDownloadingOperation.delegate = nil;
[imageDownloadingOperation release];
}
in the operation: ( and now if the imageView deallocated, its delegate will be nil, and won't crash anytime )
if (delegate)
if ([delegate respondsToSelector:#selector(handleResponse:)])
[delegate handleResponse:image];
Related
Following is my code, there is no error but selector is not responding.
Code in ExampleTableviewSubProductDetail.h
#protocol EnterAmountDelegate <NSObject>
-(void)titlechange:(NSInteger)amount;
#end
#class ASIFormDataRequest;
#interface ExampleTableviewSubProductDetail : UIViewController<UIScrollViewDelegate>
{
}
#property (nonatomic, strong) id <EnterAmountDelegate>delegate;
Code in ExampleTableviewSubProductDetail.m
#implementation ExampleTableviewSubProductDetail
#synthesize delegate;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if([delegate respondsToSelector:#selector(titlechange:)])
{
//send the delegate function with the amount entered by the user
[delegate titlechange:20];
}
code in HostProductdetailViewController.h
#import "ViewPagerController.h"
#import "ExampleTableviewSubProductDetail.h"
#interface HostProductdetailViewController : ViewPagerController <ViewPagerDataSource, ViewPagerDelegate, EnterAmountDelegate>
{
}
code in HostProductdetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.dataSource = self;
self.delegate = self;
}
-(void)titlechange:(NSInteger)amount
{
NSLog(#"sdfsf");
}
In the viewwillapper following Line always return false
if([delegate respondsToSelector:#selector(titlechange:)])
Please let me know if i am missing anything.
Thanks
When pushing from HostProductdetailViewController to ExampleTableviewSubProductDetail you need to set the exampleTableviewSubProductDetail.delegate = self
As I see some other potentially dangerous things in your code try checking this example. It consists of 2 simple classes which are connected via delegate. Watch out for strong references on delegates as this code of yours will produce a retain cycle and cause a memory leak.
Protocol:
// defining a custom protocol
#protocol PingProtocol <NSObject>
- (void)didPing;
#end
Ping class:
//
// This class will be able to send notifications via delegate for the protocol PingProtocol
// Any object that implements PingProtocol will be able to assign itself to the delegate property and will be notified to all protocol methods
//
#interface PingClass : NSObject
// The listener object that implements PingProtocol
// Note this should be weak or there will a retain cycle
#property (nonatomic, weak) id<PingProtocol> delegate;
#end
#implementation PingClass
// Some event that happens will check if the delegate actually implements this method and call it.
// The respondsToSelector is not necessary in this case since the method is not optional though.
- (void)onEvent:(id)sender
{
if([self.delegate respondsToSelector:#selector(didPing)])
{
[self.delegate didPing];
}
}
// Will create a timer which will call onEvent: every second.
// Note there should be some way to invalidate the timer as this will cause a memory leak for the PingClass
- (void)startPing
{
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(onEvent:) userInfo:nil repeats:YES];
}
#end
Listener:
//
// This class will listen to PingProtocol methods.
// It will need to implement all non-optional methods defined by PingProtocol
//
#interface ListenerClass : NSObject<PingProtocol>
#property (nonatomic, strong) PingClass *someClass;
#end
#implementation ListenerClass
// will create a PingClass object and asign itself as a delegate to start listening to delegate methods
- (void)startListening
{
self.someClass = [[PingClass alloc] init];
self.someClass.delegate = self;
[self.someClass startPing];
}
// A protocol method
- (void)didPing
{
NSLog(#"Ping");
}
#end
Most likely you are missing self:
if([self.delegate respondsToSelector:#selector(titlechange:)])
You need to watch out for these things. The delegate in your case is closer to a function pointer then an actual object. You might also be able access it via _delegate as well.
I'm new to Objective-C (from java background), so apologies if this question is too trivial.
Suppose i have two classes, where one holds a reference to another, as such:
#interface PostOffice
#property (nonatomic, strong) MailGuy *mailGuy;
#end
#implementation PostOffice
-(void)getMailmanToSendMail {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.mailGuy = [[MailGuy alloc] init];
[self.mailGuy sendMail];
}
}
#end
and for MailGuy:
#interface MailGuy () <MFMailComposeViewControllerDelegate>
#end
#implementation MailGuy
-(void)sendMail {
NSLog(#"send mail");
[self.viewController presentViewController:mailViewController animated:YES completion:nil];
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
// upon dismissal, how do i get the PostOffice instance to release this MailGuy instance?
}
#end
How do i get the PostOffice to release the MailGuy?? i only know when it should be free based on the callback. but i don't want to store a reference to the PostOffice? or do i ? and does it matter that i'm instantiating the MailGuy from a background thread?
any help would be appreciated. thanks!
The usual way to do so is to use protocol and delegates.
So in your MailGuy.h you should add a protocol
#protocol MailGuyDelegate
- (void) didPostHisLetter;
#end
And still in the .h file but this time INSIDE the #interface you would add a delegate reference
#property (weak, nonatomic) id <MailGuyDelegate> delegate;
This adds a delegate property to your MailGuy and it says that the delegate must implement the given protocol (which has the method).
Then in your mail guy implementation code here's what you would do
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
if (self.delegate) {
[self.delegate didPostHisLetter];
}
}
This tells his delegate "Hey I finished my job". So all you have to do now is implement the delegate in your PostOffice class
In your .m file of PostOffice, add a private property
#interface PostOffice() <MailGuyDelegate>
#end
Then when you invoke your mail guy, you associate it's delegate to self. Notice that I remove the async dispatch as it is not used and may cause problems as mentioned in comments
-(void)getMailmanToSendMail {
self.mailGuy = [[MailGuy alloc] init];
self.mailGuy.delegate = self;
[self.mailGuy sendMail];
}
And all is left to do is implement the protocol's method (still in postoffice implementation)
- (void) didPostHisLetter {
self.mailGuy = nil;
}
In some reason, I have to use a design pattern that an object remove itself from its container, consider the following code(ARC is enabled, LCDObject is an object class, LCDContainer is a container class), in the whole program, object 's reference count is always 1 until it is removed from the container(reference count become 0), as comment 2 mention, when [_container removeObject:self] return, the object's reference count is 0, it is dealloc, right?, but the process is still in the object's method -- "removeFromContainer", what would happen? Does the following code would be execute successfully? Does "removeFromContainer" can return successfully?
I run this code in Xcode, the "NSLog" in "removeFromContainer" can be invoked successfully, but I can't figure out why...
//-------------------------------------------------------------
#import <Foundation/Foundation.h>
#interface LCDContainer : NSObject
#property (strong, nonatomic) NSMutableArray *objects;
- (void)removeObject:(id)object;
- (id)addObject:(id)object;
#end
#implementation LCDContainer
- (id)init {
self = [super init];
if (self) {
_objects = [[NSMutableArray alloc] init];
}
return self;
}
- (id)addObject:(id)object {
[_objects addObject:object];
return object;
}
- (void)removeObject:(id)object {
[_objects removeObject:object];
}
#end
//-------------------------------------------------------------
#interface LCDObject : NSObject
#property (weak, nonatomic) LCDContainer *container;
- (id)initWithContainer:(LCDContainer*) container;
- (void)removeFromContainer;
#end
#implementation LCDObject
- (id)initWithContainer:(LCDContainer *)container {
self = [super init];
if (self) {
_container = container;
// (1) add the object to the Container, now its reference count is 1
//
[container addObject:self];
NSLog(#"add to container.");
}
return self;
}
- (void)removeFromContainer {
// (2) remove the object from the Container, now its reference count is 0,
// the object is delete, does the following "NSLog" would be invoked successfully?
//
[_container removeObject:self];
NSLog(#"remove from container.");
}
#end
//-------------------------------------------------------------
int main(int argc, const char * argv[]) {
#autoreleasepool {
LCDContainer *container = [[LCDContainer alloc] init];
[[LCDObject alloc] initWithContainer:container];
[[[container objects] objectAtIndex:0] removeFromContainer];
}
return 0;
}
I've not tried your code but I suspect it will work. sapi's suggestion (adding a breakpoint or NSLog on dealloc) is a good one to confirm, though.
Thee are two ways that ARC could be doing this. You can check the assembler if you're really interested.
The simplest is to assume that it's using autorelease, that is, when an object is removed from your container it gets added to the autorelease pool and is released (and dealloced) at the end of the current run loop.
The other way is to consider where ARC adds its retains and releases. This question notes that your remove method really looks like this:
- (void)removeObject:(id)object {
[object retain];
[_objects removeObject:object];
[object release];
}
The call to removeObject: may well have the same logic. This means that object does not get released as soon as the removeObject: call is completed; the object lifecycle is almost certainly (slightly) longer than that.
I have one class and one view controller. class has _delegate respondsToSelector:selector. But it is not calling the method(selector). After viewing my code below, would you please tell what am I doing wrong. FYI, This code worked before, but suddenly stopped working.
MyClass.h:
#protocol MyClassDelegate <NSObject>
#optional
-(void) receivedEvent:(TripEvent *) event;
#end
#interface MyClass : NSObject<MyClassEngine>
#property(assign) id<MyClassDelegate> delegate;
#property BOOL delegateOnMainThread;
-(id) initWithDelegate:(id<UbiTripRecorderDelegate>) tripDelegate
onMainThread:(BOOL) onMainThread;
- (void) dispatchDelegate:(SEL)selector
withObject:(id)obj;
MyClass.m contains the following code:
#import "MyClass.h"
#implementation MyClass
....
....
-(void) startPlaying {
[self dispatchDelegate:#selector(receivedEvent:) withObject:newEvent];
}
- (void) dispatchDelegate:(SEL)selector withObject:(id)obj {
#try {
if(_delegate && [_delegate respondsToSelector:selector]) {
NSLog(#"I can't come to this point");
}
}
}
I've also added the following code in my view controller (MyViewController.h):
#interface MyViewController : BaseViewController<MyClassDelegate>
And finally implemented the method in MyViewcontroller.m :
-(void) receivedEvent : (TripEvent *) event {
NSLog(#"I'm called");
}
The problem is that the if(_delegate && [_delegate respondsToSelector:selector]) - logic never return true. Can anybody please check the code and let me know, if anything is wrong with my code?
Make sure your _delegate is not nil, otherwise the if statement is never true.
Also you can simplify
if(_delegate && [_delegate respondsToSelector:selector])
to
if([_delegate respondsToSelector:selector])
because message send to nil always return 0 or NO. In this case [_delegate respondsToSelector:selector] will return NO if _delegate is nil
Also, you did not declare a method in header will not cause any runtime difference. It may have compiler warning/error if such method not found.
I just started to learn obj-c and I have question about delegates. I know that on SOF is a lot of similar threads, but I was looking for and really didn't get my issue (maybe cause I'm beginner). Here's my problem: I want to use my own delegate and transfer an information from SlaveClass to MainClass. In SlaveClass in buttonDidClick: action, I declare delegate which is equal to NIL. Even I don't know where I should start to looking for mistake. Thanks in advance for any type of advice. Here's my code which refer to delegate:
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m (here appears NIL)
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}
MainClass.h
#interface MainClass : UIViewController <slaveDelegate>
#end
MainClass.m
-(void)transferNameDidClick:(NSString *)text
{
SlaveClass *delegate = [[SlaveClass alloc] init];
[delegate setMyOwnDelegate:self];
[_label setText:text];
NSLog(#"text: %#",text);
}
You are setting your delegate in wrong place. You have to set the delegate before going to slaveClass
mainClass.m
Present slave view controller like this
SlaveClass *slaveClass = [[SlaveClass alloc] init]
[slaveClass setMyOwnDelegate:self];
[self presentViewController:slaveClas animated:YES completion:nil];
-(void)transferNameDidClick:(NSString *)text
{
// This is the method getting called by the slaveClass. So it should know the delegate to call this.
[_label setText:text];
NSLog(#"text: %#",text);
}
Set you delegate out side the Custom delegate method. you are mistakenly setting inside the custom delegate method thats y delegate show nil. use like this.
Main Class .M
-(IBAction)nextView
{
nextView = [[ViewController2 alloc]init];
nextView.myOwnDelegate = self;
[self presentViewController:nextView animated:YES completion:nil];
}
-(void)transferNameDidClick:(NSString *)text;
{
NSLog(#"Value from Slave Delegate %#",text);
}
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}