Obj-C, How do I use a category to supply methods which I will use in delegate methods?
I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
@interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
@property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
@end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
@implementation UIViewController (CloudKitDelegates)
@dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(@"%@", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
@end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: @"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
objective-c cocoa-touch delegates objective-c-category
|
show 1 more comment
I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
@interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
@property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
@end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
@implementation UIViewController (CloudKitDelegates)
@dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(@"%@", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
@end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: @"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
objective-c cocoa-touch delegates objective-c-category
1
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured thatUIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).
– Rob Napier
Dec 7 '18 at 0:07
How isiCloudDBDelegate
instantiated? DoUIViewController
andiCloudDBDelegate
both conform toiCloudDBDelegate
? DoesStartup
conform toiCloudDBDelegate
?
– Willeke
Dec 7 '18 at 0:14
Which object is the delegate,iCloudDBDelegate
or the view controller? Which object should executecrashLog:
?
– Willeke
Dec 7 '18 at 9:50
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30
|
show 1 more comment
I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
@interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
@property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
@end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
@implementation UIViewController (CloudKitDelegates)
@dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(@"%@", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
@end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: @"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
objective-c cocoa-touch delegates objective-c-category
I want to provide methods used in several view controllers called in my delegate methods.
For example, I have some CloudKit functionality (I've added this to my own framework, but I don't think thats important), where I want to provide some crash logging.
Previosuly I had a crashLog function in each of my view controllers, which worked fine, but I have a lot of duplicate code.
Therefore I'd like to produce a category with these methods instead.
However I'm having difficulty getting my delegate methods to see these category methods.
Here's my code..
UIViewController+CloudKitDelegates.h
@interface UIViewController (CloudKitDelegates) <iCloudDBDelegate>
@property (weak,nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...;
@end
UIViewController+CloudKitDelegates.m
#import "UIViewController+CloudKitDelegates.h"
@implementation UIViewController (CloudKitDelegates)
@dynamic iCloudDBDelegate;
-(void)crashLog:(NSString*)message, ...
{
va_list args;
va_start(args, message);
NSLog(@"%@", [[NSString alloc] initWithFormat:message arguments:args]);
va_end(args);
}
@end
h file - my calling view controller (e.g. My View Controller)
#import "UIViewController+CloudKitDelegates.h"
m file - delegate method
-(NSString*)getDBPath
{
[self.iCloudDBDelegate crashLog: @"testing"];
From this call I'm getting an error ...
'NSInvalidArgumentException', reason: '-[MyViewController crashLog:]:
unrecognized selector sent to instance
The error is showing that my calling view controller called MyViewController doesn't have the crashLog method, which I have in my category.
Any ideas where I'm going wrong ?
objective-c cocoa-touch delegates objective-c-category
objective-c cocoa-touch delegates objective-c-category
edited Dec 28 '18 at 23:10
BillThomas
asked Dec 6 '18 at 22:57
BillThomasBillThomas
175
175
1
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured thatUIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).
– Rob Napier
Dec 7 '18 at 0:07
How isiCloudDBDelegate
instantiated? DoUIViewController
andiCloudDBDelegate
both conform toiCloudDBDelegate
? DoesStartup
conform toiCloudDBDelegate
?
– Willeke
Dec 7 '18 at 0:14
Which object is the delegate,iCloudDBDelegate
or the view controller? Which object should executecrashLog:
?
– Willeke
Dec 7 '18 at 9:50
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30
|
show 1 more comment
1
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured thatUIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).
– Rob Napier
Dec 7 '18 at 0:07
How isiCloudDBDelegate
instantiated? DoUIViewController
andiCloudDBDelegate
both conform toiCloudDBDelegate
? DoesStartup
conform toiCloudDBDelegate
?
– Willeke
Dec 7 '18 at 0:14
Which object is the delegate,iCloudDBDelegate
or the view controller? Which object should executecrashLog:
?
– Willeke
Dec 7 '18 at 9:50
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30
1
1
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured that
UIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).– Rob Napier
Dec 7 '18 at 0:07
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured that
UIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).– Rob Napier
Dec 7 '18 at 0:07
How is
iCloudDBDelegate
instantiated? Do UIViewController
and iCloudDBDelegate
both conform to iCloudDBDelegate
? Does Startup
conform to iCloudDBDelegate
?– Willeke
Dec 7 '18 at 0:14
How is
iCloudDBDelegate
instantiated? Do UIViewController
and iCloudDBDelegate
both conform to iCloudDBDelegate
? Does Startup
conform to iCloudDBDelegate
?– Willeke
Dec 7 '18 at 0:14
Which object is the delegate,
iCloudDBDelegate
or the view controller? Which object should execute crashLog:
?– Willeke
Dec 7 '18 at 9:50
Which object is the delegate,
iCloudDBDelegate
or the view controller? Which object should execute crashLog:
?– Willeke
Dec 7 '18 at 9:50
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30
|
show 1 more comment
2 Answers
2
active
oldest
votes
The problem: identical method crashLog:
in multiple classes, for example
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
Solution A: move crashLog:
to a common superclass (or a category on superclass UIViewController
)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
Solution B: move crashLog:
to a delegate and protocol
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
Now we have new problem: property iCloudDBDelegate
in multiple classes
Solution B + A: move crashLog
to a delegate, move iCloudDBDelegate
property to a superclass
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults
or NSFontManager.sharedFontManager
: CloudDBManager.sharedCloudDBManager
. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager
from everywhere.
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" becauseself.iCloudDBDelegate
isnil
. You have to instantiate a delegate object and assign it toiCloudDBDelegate
.
– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
|
show 3 more comments
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC
in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC
flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither hasStartup
. If-[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, orStartup
is not aUIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.
– Rob Napier
Dec 10 '18 at 19:34
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53660942%2fobj-c-how-do-i-use-a-category-to-supply-methods-which-i-will-use-in-delegate-me%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
The problem: identical method crashLog:
in multiple classes, for example
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
Solution A: move crashLog:
to a common superclass (or a category on superclass UIViewController
)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
Solution B: move crashLog:
to a delegate and protocol
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
Now we have new problem: property iCloudDBDelegate
in multiple classes
Solution B + A: move crashLog
to a delegate, move iCloudDBDelegate
property to a superclass
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults
or NSFontManager.sharedFontManager
: CloudDBManager.sharedCloudDBManager
. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager
from everywhere.
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" becauseself.iCloudDBDelegate
isnil
. You have to instantiate a delegate object and assign it toiCloudDBDelegate
.
– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
|
show 3 more comments
The problem: identical method crashLog:
in multiple classes, for example
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
Solution A: move crashLog:
to a common superclass (or a category on superclass UIViewController
)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
Solution B: move crashLog:
to a delegate and protocol
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
Now we have new problem: property iCloudDBDelegate
in multiple classes
Solution B + A: move crashLog
to a delegate, move iCloudDBDelegate
property to a superclass
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults
or NSFontManager.sharedFontManager
: CloudDBManager.sharedCloudDBManager
. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager
from everywhere.
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" becauseself.iCloudDBDelegate
isnil
. You have to instantiate a delegate object and assign it toiCloudDBDelegate
.
– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
|
show 3 more comments
The problem: identical method crashLog:
in multiple classes, for example
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
Solution A: move crashLog:
to a common superclass (or a category on superclass UIViewController
)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
Solution B: move crashLog:
to a delegate and protocol
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
Now we have new problem: property iCloudDBDelegate
in multiple classes
Solution B + A: move crashLog
to a delegate, move iCloudDBDelegate
property to a superclass
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults
or NSFontManager.sharedFontManager
: CloudDBManager.sharedCloudDBManager
. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager
from everywhere.
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
The problem: identical method crashLog:
in multiple classes, for example
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
Solution A: move crashLog:
to a common superclass (or a category on superclass UIViewController
)
@interface CommonViewController : UIViewController
-(void)crashLog:(NSString *)message;
@end
@implementation CommonViewController
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self crashLog:@"error"];
}
@end
Solution B: move crashLog:
to a delegate and protocol
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : UIViewController
@end
@implementation ViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.iCloudDBDelegate = appDel.iCloudDBDelegate;
}
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
@interface AppDelegate : UIResponder <UIApplicationDelegate, AppDelProtocolDelegate, iCloudDBDelegate>
@property (strong, nonatomic) id<iCloudDBDelegate>iCloudDBDelegate;
@end
@implementation AppDelegate
- (id<iCloudDBDelegate>)iCloudDBDelegate {
if (!_iCloudDBDelegate) {
_iCloudDBDelegate = [[DelegateClass alloc] init];
}
return _iCloudDBDelegate;
}
@end
Now we have new problem: property iCloudDBDelegate
in multiple classes
Solution B + A: move crashLog
to a delegate, move iCloudDBDelegate
property to a superclass
@protocol ICloudDBDelegate
-(void)crashLog:(NSString *)message;
@end
@interface DelegateClass : AnyClass <ICloudDBDelegate>
@end
@implementation DelegateClass
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface CommonViewController : UIViewController
@property (weak, nonatomic) id <ICloudDBDelegate> iCloudDBDelegate;
@end
@implementation CommonViewController
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[self.iCloudDBDelegate crashLog:@"error"];
}
@end
Solution C:
Another approach is a singleton object like NSUserDefaults.standardUserDefaults
or NSFontManager.sharedFontManager
: CloudDBManager.sharedCloudDBManager
. No category or protocol required, just include CloudDBManager.h and use CloudDBManager.sharedCloudDBManager
from everywhere.
@interface CloudDBManager : NSObject
@property(class, readonly, strong) CloudDBManager *sharedCloudDBManager;
-(void)crashLog:(NSString *)message;
@end
@implementation CloudDBManager
+ (CloudDBManager *)sharedCloudDBManager {
static CloudDBManager *sharedInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedInstance = [[CloudDBManager alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
-(void)crashLog:(NSString *)message {
NSLog(@"%@", message);
}
@end
@interface ViewController : CommonViewController
@end
@implementation ViewController
- (void)someMethod {
[CloudDBManager.sharedCloudDBManager crashLog:@"error"];
}
@end
edited Dec 28 '18 at 16:11
answered Dec 7 '18 at 23:07
WillekeWilleke
7,67621024
7,67621024
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" becauseself.iCloudDBDelegate
isnil
. You have to instantiate a delegate object and assign it toiCloudDBDelegate
.
– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
|
show 3 more comments
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" becauseself.iCloudDBDelegate
isnil
. You have to instantiate a delegate object and assign it toiCloudDBDelegate
.
– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Is the delegate a singleton?
– Willeke
Dec 8 '18 at 22:17
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
Do all view controller share one delegate object or does each view controller have its own delegate object?
– Willeke
Dec 9 '18 at 5:47
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
In case you're still stuck, I added another solution which is less tangled and easier to implement.
– Willeke
Dec 13 '18 at 10:29
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" because
self.iCloudDBDelegate
is nil
. You have to instantiate a delegate object and assign it to iCloudDBDelegate
.– Willeke
Dec 13 '18 at 12:08
I don't know the structure of your app and framework. If your framework should call some method you provide in your app, a delegate would do. The TestDelegateProtocol project on Github doesn't log "testing" because
self.iCloudDBDelegate
is nil
. You have to instantiate a delegate object and assign it to iCloudDBDelegate
.– Willeke
Dec 13 '18 at 12:08
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
Didn't you have a startup init method which sets the iCloudDBDelegate?
– Willeke
Dec 17 '18 at 9:29
|
show 3 more comments
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC
in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC
flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither hasStartup
. If-[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, orStartup
is not aUIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.
– Rob Napier
Dec 10 '18 at 19:34
add a comment |
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC
in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC
flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither hasStartup
. If-[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, orStartup
is not aUIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.
– Rob Napier
Dec 10 '18 at 19:34
add a comment |
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC
in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC
flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
(I've added this to my own framework, but I don't think thats important)
Yep, that's the typical problem. You've failed to include -ObjC
in the link flags.
See Building Objective-C static libraries with categories. This applies to frameworks as well.
ObjC does not create linker symbols for methods. It can't, they're not resolved until runtime. So the category methods aren't seen by the linker as "missing" and it doesn't bother linking the relevant compile unit. This is an important optimization that keeps you from linking all of a massive C library just because you use one function in it, but Objective-C categories break some of the linker's assumptions. The compiler saw the definition (via the header), but the linker didn't care, so there's no error until runtime.
The -ObjC
flag says "this C-looking compile unit is actually Objective-C; link all of it even if you don't think you need to."
answered Dec 10 '18 at 14:07
Rob NapierRob Napier
200k28294421
200k28294421
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither hasStartup
. If-[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, orStartup
is not aUIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.
– Rob Napier
Dec 10 '18 at 19:34
add a comment |
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither hasStartup
. If-[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, orStartup
is not aUIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.
– Rob Napier
Dec 10 '18 at 19:34
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither has
Startup
. If -[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, or Startup
is not a UIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.– Rob Napier
Dec 10 '18 at 19:34
The code you posted above and the code you're put on GitHub don't have any of the things you're describing. Neither has
Startup
. If -[Startup crashLog:]
fails with "unrecognized selector sent to instance" then you're not loading the code you think you're loading, which almost certainly means you're not linking what you think you're linking, or Startup
is not a UIViewController
, or the code is running before categories are loaded (which is somewhat late in the launch). I suggest reducing this to an example that fails in the way you're describing.– Rob Napier
Dec 10 '18 at 19:34
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53660942%2fobj-c-how-do-i-use-a-category-to-supply-methods-which-i-will-use-in-delegate-me%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Is the category in a framework? That's the usual cause of this kind of problem. Alternately have you ensured that
UIViewController+CloudKitDelegates.m
is actually being compiled and included in the project (another common mistake).– Rob Napier
Dec 7 '18 at 0:07
How is
iCloudDBDelegate
instantiated? DoUIViewController
andiCloudDBDelegate
both conform toiCloudDBDelegate
? DoesStartup
conform toiCloudDBDelegate
?– Willeke
Dec 7 '18 at 0:14
Which object is the delegate,
iCloudDBDelegate
or the view controller? Which object should executecrashLog:
?– Willeke
Dec 7 '18 at 9:50
See @RobNapier's comment -- you said your code was in a framework, & he said putting that code in a framework often causes these problems. So consider just moving your category out of the framework and into your app for starters.
– Caleb
Dec 10 '18 at 12:28
Also: I see the word delegate being thrown around here rather a lot, but I don't see an actual delegate object. People misuse delegate all the time, and it just seems to be clouding the issue here. Is delegation an important aspect of your problem?
– Caleb
Dec 10 '18 at 12:30