Adding the same category to multiple classes
Asked Answered
E

6

20

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.

Emmaline answered 26/2, 2012 at 4:30 Comment(2)
I don't mean to hijack this question, but perhaps this will add context. I have the same need (one category, multiple classes) but for two non-parallel classes: a concrete model with full implementation and an NSProxy for that model with a shared dynamic implementation. Since I want both to have the same interface, I need the exact same property declarations in both @interfaces. The model's implementation has custom setters/getters while the proxy's has only @dynamic declarations.Gallup
NM, that was stupid. A proxy wouldn't need an implementation, only the interface.Gallup
E
1

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
Emmaline answered 11/8, 2015 at 21:28 Comment(0)
L
5

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

Lactation answered 10/9, 2013 at 18:9 Comment(0)
L
2

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.

Lungworm answered 26/2, 2012 at 4:47 Comment(1)
This is a good idea, though using a shared separate class with class-level methods is adding one more level of abstraction, removing the actual logic away from the location where it makes sense to define it.Emmaline
E
1

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
Emmaline answered 11/8, 2015 at 21:28 Comment(0)
E
0

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

Excursion answered 26/2, 2012 at 4:42 Comment(0)
B
0

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.

Beera answered 26/2, 2012 at 5:34 Comment(0)
C
0

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.

Cointreau answered 26/2, 2012 at 6:7 Comment(3)
If you declare a category on NSManagedObject (@interface NSManagedObject (MyAdditionalGoodies)), then you are modifying NSManagedObject within your program. You're at least adding methods, possibly replacing some.Perineuritis
Edit: I removed the class checking code from my question as it wasn't relavent to the actual question.Emmaline
Thanks for the correction Peter. What appears to really happen is explained here: #7026759Cointreau

© 2022 - 2024 — McMap. All rights reserved.