Can't get custom @protocol working on iOS
Note: the below is using iOS with Automatic Reference Counting (ARC) enabled. I think ARC may have a lot to do with why it isn't working as this is set up as per examples i've found via google.
I am trying to create a protocol to notify a delegate of the filename the user selects from a UITableView.
FileListViewController.h
@protocol FileListDelegate <NSObject>
- (void)didSelectFileName:(NSString *)fileName;
@end
@interface FileListViewController : UITableViewController
{
@private
NSArray *fileList;
id <FileListDelegate> delegate;
}
@property (nonatomic, retain) NSArray *fileList;
@property (nonatomic, assign) id <FileListDelegate> delegate;
@end
FileListViewController.m
#import "FileListViewController.h"
@implementation FileListViewController
@synthesize fileList;
@synthesize delegate;
This gives an error at the
@synthesize delegate;
line which is "FileListViewController.m: error: Automatic Reference Counting Issue: Existing ivar 'delegate' for unsafe_unretained property 'delegate' must be __unsafe_unretained"
If i change FileListViewController.h putting __weak and (weak) then it will run.
@protocol FileListDelegate <NSObject>
- (void)didSelectFileName:(NSString *)fileName;
@end
@interface FileListViewController : UITableViewController
{
@private
NSArray *fileList;
__weak id <FileListDelegate> delegate;
}
@property (nonatomic, retain) NSArray *fileList;
@property (weak) id <FileListDelegate> delegate;
@end
But when I try to set the delegate the app crashes. A view called 'ImportViewController' is creating a view from 'FileListViewController' and setting the delegate to itself (ImportViewController) so I can implement my custom protocol of 'didSelectFileName'. The error I get is;
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[ImportViewController setDelegate:]: unrecognized selector 开发者_如何学编程sent to instance 0x6c7d430'
The code I am running is;
ImportViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
FileListViewController *fileListViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"filelist"];
[fileListViewController setDelegate:self];
[self.navigationController pushViewController:fileListViewController animated:YES];
}
My Questions are:
- Why does putting (weak) and __weak in make it work? I don't understand why this works as I found it googling and there wasn't an explanation.
- Why can't I set my delegate using this '[fileListViewController setDelegate:self];' ? It seems like the compiler doesn't know 'delegate' exists.
Under ARC ivars default to strong
. So the error
Automatic Reference Counting Issue: Existing ivar 'delegate' for unsafe_unretained property 'delegate' must be __unsafe_unretained"
is telling you that you've declared a property with __unsafe_unretained
(assign) ownership, where the underlying ivar has __strong
ownership, which is illegal. To avoid the error, you have 3 options:
- Omit the ivar. It's not necessary to declare an ivar for a synthesized property. The ivar will be declared implicitly with ownership matching your property.
- Define the ivar to match your (assign) property declaration:
__unsafe_unretained id <FileListDelegate> delegate;
- Define the property to match the ivar's implicit __strong ownership:
@property (weak) id <FileListDelegate> delegate;
Personally, I'd omit the ivar declaration so you have the ownership semantics in one place, on the property declaration.
It seems that with :
FileListViewController *fileListViewController =
[self.storyboard instantiateViewControllerWithIdentifier:@"filelist"];
you didn't get an FileListViewController
object. Look at the message it says :
-[ImportViewController setDelegate:]: unrecognized selector sent to instance 0x6c7d430
and that why your app crashes. Also try to define a retain property, instead of just assign, in case the delegate is deallocated elsewhere, your app won't crash.
I just ran across this same issue, forcing me to finally delve into the ARC documentation.
Also try to define a retain property, instead of just assign, in case the delegate is deallocated elsewhere, your app won't crash.
To maybe clarify the above quote from user756245 's answer, based on my reading I don't think that iOS 5 has changed the best practice that you shouldn't be retaining your delegate as this is a good way to leak. I think the __weak and (weak) tokens are annotations for the compiler for the sake of being able to correctly deal with generating code for the delegate.
精彩评论