I have an Objective-C category that I'd like to add to multiple classes without duplicating the code contained in the category. I simply want to add the same methods to multiple classes.
I have existing categories on NSManagedObject
subclasses (Book
, Chapter
, Page
) and I would like to add common functionality throughout these subclasses in a clean and maintainable way.
One way would be to add the category to their common superclass (NSManagedObject
), but that has the consequence of adding the category's methods to all NSManagedObject
subclasses when I want to add the methods to three NSManagedObject subclasses (Book
, Chapter
, Page
).
Another solution would be to subclass NSManagedObject
and then have Book
, Chapter
, and Page
inherit from that NSManagedObject
subclass. This is the cleanest, most straight forward approach. The big downside with this approach is when the data model changes and Xcode regenerates the subclasses, it will reset them back to inheriting from NSManagedObject
instead of SubclassedManagedObject
. I'd like to avoid using something like mogenerator/Xmo'd if possible.
Is it possible to add a single category on multiple classes without duplicating code?
Thanks.
maybe it's too late.. But maybe there is one way to do it..
But, you said.. needs to have the same superclass
Category.h
@protocol MyProtocol <NSObject>
- (NSString*)foo;
@end
@interface NSArray (category) <MyProtocol> @end
@interface NSString (category) <MyProtocol> @end
Category.m
@interface NSObject (category) <MyProtocol> @end
@implementation NSObject (category)
- (NSString*)foo
{
return @"bar";
}
@end
I don't like this neither, but it works
Why not make the shared code class level methods in a central class, that you simply call via shell methods in each of your categories?
If your categories are storing associated references you could pass those into the class level methods to act on.
I'm still unaware of a clean way to do this in Objective-C, but with Swift 2.0 this can be implemented using Protocol Extensions by adding functions and/or properties to an existing protocol. The protocol can then be adopted by an arbitrary number of classes, structs, and/or enums.
protocol Numbered {
func number() -> Int
}
extension Numbered {
func number() -> Int {
return Int(arc4random()) % 10
}
}
class Book : Numbered {
}
class Chapter : Numbered {
}
class Page : Numbered {
}
let myBook = Book()
let myChapter = Chapter()
let myPage = Page()
print("myBook.number() = \(myBook.number())")
print("myChapter.number() = \(myChapter.number())")
print("myPage.number() = \(myPage.number())")
correctly implements number()
on all three classes (Book
, Chapter
, Page
):
myBook.number() = 5
myChapter.number() = 2
myPage.number() = 8
For the rest of your stuff there, as far as I know you would have to go back and make a common subclass for your three classes to get what you want. But what I can point out is that instead of doing your own isSupported method there it would probably be better to simply use the respondsToSelector method of NSObject to tell if your class implements whatever special method you want those three classes to use, which should be better than checking against all those classes. Defiantly better if you add additional classes as you don't have to maintain or expand that giant list of isMemberOfClass checks
It sounds kind of like you want something like a ruby module. I don't know of any way to do such a thing in objective-c. You could make a protocol and make each of your classes conform to your protocol, but that doesn't solve the problem of sharing implementation of the methods.
Check out this question, it might provide some more insights.
It's a bit of a misnomer to say that providing a category on nsmanagedobject "has the unintended consequence of adding the category's methods to all NSManagedObject subclasses.". The category code is just linked when you include it in a file in which you are using it: you aren't modifying nsmanagedobject.
That said, if the code needs to be aware of its object, you could create a protocol to which those classes conform, and then use conformsToProtocol in your code to do the testing. That's probably a better generic approach than testing for specific class types.