i am on objective-c, macOS.
I am currently rewriting an application to support NSSecureCoding Protocol.
In some classes i have some properties like this
#property id objectValue;
These objects can be of very dynamic kind (NSString, custom object ...)
On loading i did this before:
_objectValue = [coder decodeObjectForKey:#"objectValue"];
With NSSecureCoding i need to specify the class which i cannot do with id obviously.
So here are my questions:
Is it OK to pass a superclass like NSObject even if it's basically an NSString?
_objectValue = [coder decodeObjectOfClass:[NSObject class] forKey:#"objectValue"];
But... doesn't this counteract the idea of NSSecureCoding?
Is there another (better) approach?
Edit:
Just seen that this is possible with multiple classes decodeObjectOfClasses:forKey: - would the correct approach be to list all classes possible?
Related
Given this protocol definition:
#protocol MyProtocol <NSObject>
#property (nonatomic, strong) NSString *someProperty;
#end
Why will Xcode gladly offer autocompletion for this statement:
id<MyProtocol> thing = [ThingManager currentThing];
[thing someProperty]; // Xcode offered autocompletion here
But it doesn't offer autocompletion when I try to access the same property using dot-notation:
id<MyProtocol> thing = [ThingManager currentThing];
thing.someProperty; // Xcode claimed there were
// "No completions" available
// after the period
Because id is a base type, Xcode and CLANG are uneasy about providing dot-syntax access against it because dot syntax is just syntactic sugar for a method call to an associated setter or getter in a normal object, but id has no defined method members. Looking at it from the C side of things, id is a typedef for a struct pointer that the compiler cannot see the members of, which means it cannot access them (never mind the fact that you would need to dereference id before dot-access would make any semantic sense).
Back to the Objective-C side of things, protocols don't actually add methods or properties to the classes that claim to implement them, rather they serve as a specifier to other classes that an object that conforms to a given protocol implements a series of methods. As for the method-syntax being completed, Xcode pools all of the given methods of all the files imported into a given .m file because, an object of type id can receive any message*
*of course, it can receive the message, but it'll still crash if it's unimplemented.
This is kind of a tangential answer, and a thought experiment.
But before that, I'll note that you could get your property autocomplete by skipping id, like this:
NSObject<MyProtocol> *thing;
thing.▮
But assuming you don't want the entire list of NSObject methods gumming up your completion, you could do something like
EmptyClass<MyProtocol> *thing = [ThingManager currentThing];
// completion list will be (close) to only the protocol props
thing.▮
EmptyClass serves a similar "OK, no promises!" role that id does, but autocomplete likes it. Here's EmptyClass:
NS_ROOT_CLASS
#interface EmptyClass
#end
#implementation EmptyClass
+ (void)initialize {} // required
#end
Mind you, the object in thing is not actually rooted on EmptyClass (can't be), so this is high fakery. However, it
vastly underpromises what thing can actually do.
doesn't (can't!) instantiate an EmptyClass object.
So why not? If you try really hard, you can cause problems like
EmptyClass *nooooo = [[NSClassFromString(#"EmptyClass") alloc] init];
which will immediately exception. But not really a tricky bug to avoid.
A gotcha wouldn't surprise me, but I don't know one right now. Please, leave a comment if you do.
I need to have a class, which has all methods of NSArray, which behave the same way, but 2 methods are modified.
I want to override these 2 methods in my custom class:
1) countByEnumeratingWithState:objects:count:
2) objectAtIndex:
After hours of research I don't see any reasonable way to do that, because:
I don't want to use category, because not all NSArray instances should have the modified behaviour. (Plus that throws warnings)
I don't want to re-write all initializers plus all arrayWith... methods + the primitive methods + implemented my own storage (because this functionality is already implemented in Cocoa, right? Why would I re-implement all the functionality of a class that is already there?)
If I have my custom class inherit NSObject and use NSArray as storage in an ivar, then all NSArray's methods are not available when programming in Xcode (even if I can forward them to the NSArray ivar)
I had some success overwriting the method implementations on demand by using method_setImplementation(...), but still can't figure out a way to have dynamically a class created at runtime, which then will have custom implementation of the 2 methods I mentioned.
Looking forward to your ideas! Thanks
Mantra: If something is hard (or seems like it requires more code than is necessary), it is likely that your design is counter to the design principals of the iOS / OS X frameworks. It may yield a better solution to revisit your design.
To answer the original question, if you want to subclass NSArray (or NSMutableArray), you need to implement the primitive methods, no more, no less.
The primitive methods are the methods declared in the #interface of the class itself. I.e.:
#interface NSArray : NSObject
- (NSUInteger)count;
- (id)objectAtIndex:(NSUInteger)index;
#end
And for NSMutableArray:
#interface NSMutableArray : NSArray
- (void)addObject:(id)anObject;
- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeLastObject;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
#end
If you subclass NSMutableArray and implement the above 7 methods (the two from NSArray, too), you will have an NSMutableArray subclass that is compatible -- assuming your methods are correctly implemented -- with all APIs that consume mutable arrays.
This is because of the way class clusters are designed. The public classes are abstract; are never directly instantiated. They provide a primitive interface that contains the class's core functionality and then concrete implementations of all the other non-primtive API (save for the initializers, see below) that are implemented in terms of the primitives. Concrete, private, subclasses then override all the primitives and some of the non-primitives to provide optimal behaviors for specific configurations.
I want to have an NSArray instance for a library I'm working on and I
want to have it working transparently for the users of my library. Ie.
for them should be no difference between using a normal NSArray and
the modified class I'll be providing. Ie. it's a storage concern,
which the end users should not be concerned with and the interface
should remain the same as NSArray - therefore loosing all init methods
is not really an option at that point.
The initialization methods are not a part of the primitive interface to NSArray. You are adding a requirement above and beyond "make a class compatible with NSArray / NSMutableArray" as defined by the documentation. Nothing wrong with that, just pointing it out.
The reason why this is the case is because it is exceptionally rare to subclass the collection classes to provide the kind of business logic you describe. Collections are very generic in their behavior whereas such business logic that conditionalizes collection behavior would be done in a class that manages the overall model layer object graph.
If you really want to do this, provide an implementation of whatever init* methods you want, calling through to your wrapped generic instance as needed. There isn't anything so special about the implementations of the initializers that you are going to lose much in doing so.
No need to implement all of them, either. Implement one or two and #throw a descriptive exception on the rest.
If you do decide to forward the ones that accept var-args, you can't directly because there are no va_list accepting methods. Instead, you'll want to convert the va_list of arguments into a language array (i.e. id[] foo = malloc(... * sizeof(id));) and pass it to initWithObjects:count:.
Some other comments:
What you are doing [provide full NS*Array interface in a subclass] seems hard because it is not a common pattern and the framework designers saw no need to create a design to support it. Custom behaviors at the primitive collection levels are almost always better implemented at a higher level within the object graph. Almost always.
method_setImplementation() and dynamic class creation is academically interesting, but pretty much never a solution. Obviously, mucking with the NSArray or NSMutableArray classes (or the concrete implementation classes) is going to blow up the rest of the frameworks that rely upon standard behavior. Beyond that it, it is a pattern of dynamic OO composition that is not really intended to be used in Objective-C; it'll be a pain in the ass to maintain.
Instead of subclassing NSArray why not create a new class based on NSObject that contains an NSArray?
Then you can use all the functions of the NSArray and add your own methods that will do custom actions with it?
Or do you NEED an NSArray?
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.
I've had various cases where an Objective-C class has a property that needs to be a collection class (NSArray usually). Is there a standard way to implement this? It would be great to be able to just use #synthesize to set this up. I could just declare the property as NSMutableArray and #synthesize that, but that doesn't allow me to enforce what types of objects can be placed into the collection, nor does it prevent the client code from modifying the array. What I typically do is something like this:
#property(nonatomic, readonly) NSArray *widgets;
- (void)addWidget:(Widget*)widget;
- (void)removeWidget:(Widget*)widget;
...
The collection is implemented as an NSMutableArray, with an NSArray containing the current contents passed back to the caller. This seems like a lot of coding for what must be a common scenario. Even more coding needs to be done in order to set up key-value observing.
Am I missing something, or is it really this much to work to set up a collection property?
You can prevent client code from modifying your array by declaring the property as an NSArray, but using an NSMutableArray as the storage mechanism. The #property and #synthesize directives will still work.
There isn't really a good way to ensure type-safety of the objects returned by your array, but Objective-C programmers almost never worry about that. Its just not a common concern. If you really want to ensure that only objects of a certain type go into and come out of your array, you're going to have to write a wrapper class for NSArray to do so.
In Objective-C, I can add methods to existing classes with a category, e.g.
#interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
#end
Is it also possible to do this with protocols, i.e. if there was a NSString protocol, something like:
#interface <NSString> (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
#end
I want to do this since I have several extensions to NSObject (the class), using only public NSObject methods, and I want those extensions also to work with objects implementing the protocol .
To give a further example, what if I want to write a method logDescription that prints an object's description to the log:
- (void) logDescription {
NSLog(#"%#", [self description]);
}
I can of course add this method to NSObject, but there are other classes that do not inherit from NSObject, where I'd also like to have this method, e.g. NSProxy. Since the method only uses public members of protocol , it would be best to add it to the protocol.
Edit: Java 8 now has this with "virtual extension methods" in interfaces: http://cr.openjdk.java.net/~briangoetz/lambda/Defender%20Methods%20v4.pdf. This is exactly what I would like to do in Objective-C. I did not see this question earning this much attention...
Regards,
Jochen
Short answer: No.
Long answer: how would this work? Imagine you could add methods to existing protocols? How would this work? Imagine we wanted to add another method to NSCoding, say -(NSArray *) codingKeys; This method is a required method that returns an array of the keys used to encoding the object.
The problem is that there are existing classes (like, say NSString) that already implement NSCoding, but don't implement our codingKeys method. What should happen? How would the pre-compiled framework know what to do when this required message gets sent to a class that does not implement it?
You could say "we can add the definition of this method via a category" or "we could say that any methods added via these protocol categories are explicitly optional". Yes, you could do this and theoretically get around the problem I've described above. But if you're going to do that, you might as well just make it a category in the first place, and then check to make sure the class respondsToSelector: before invoking the method.
While it's true that you can't define categories for protocols (and wouldn't want to, because you don't know anything about the existing object), you can define categories in such a way that the code only applies to an object of the given type that has the desired protocol (sort of like C++'s partial template specialization).
The main use for something like this is when you wish to define a category that depends on a customized version of a class. (Imagine that I have UIViewController subclasses that conform to the Foo protocol, meaning they have the foo property, my category code may have need of the foo property, but I can't apply it to the Foo protocol, and if I simply apply it to UIViewController, the code won't compile by default, and forcing it to compile means someone doing introspection, or just screwing up, might call your code which depends on the protocol. A hybrid approach could work like this:
#protocol Foo
- (void)fooMethod
#property (retain) NSString *foo;
#end
#implementation UIViewController (FooCategory)
- (void)fooMethod {
if (![self conformsToProtocol:#protocol(Foo)]) {
return;
}
UIViewController<Foo> *me = (UIViewController<Foo>*) self;
// For the rest of the method, use "me" instead of "self"
NSLog(#"My foo property is \"%#\"", me.foo);
}
#end
With the hybrid approach, you can write the code only once (per class that is supposed to implement the protocol) and be sure that it won't affect instances of the class that don't conform to the protocol.
The downside is that property synthesis/definition still has to happen in the individual subclasses.
extObjC has the NEATEST stuff you can do with Protocols / Categories... first off is #concreteprotocol...
Defines a "concrete protocol," which can provide default implementations of methods within protocol.
An #protocol block should exist in a header file, and a corresponding #concreteprotocol block in an implementation file.
Any object that declares itself to conform to this protocol will receive its method implementations, but only if no method by the same name already exists.
MyProtocol.h
#protocol MyProtocol
#required - (void)someRequiredMethod;
#optional - (void)someOptionalMethod;
#concrete - (BOOL)isConcrete;
MyProtocol.m
#concreteprotocol(MyProtocol) - (BOOL)isConcrete { return YES; } ...
so declaring an object MyDumbObject : NSObject <MyProtocol> will automatically return YES to isConcrete.
Also, they have pcategoryinterface(PROTOCOL,CATEGORY) which "defines the interface for a category named CATEGORY on a protocol PROTOCOL". Protocol categories contain methods that are automatically applied to any class that declares itself to conform to PROTOCOL." There is an accompanying macro you also have to use in your implementation file. See the docs.
Last, but NOT least / not directly related to #protocols is
synthesizeAssociation(CLASS, PROPERTY), which "synthesizes a property for a class using associated objects. This is primarily useful for adding properties to a class within a category. PROPERTY must have been declared with #property in the interface of the specified class (or a category upon it), and must be of object type."
So many of the tools in this library open (way-up) the things you can do with ObjC... from multiple inheritance... to well, your imagination is the limit.
It isn't really meaningful to do so since a protocol can't actually implement the method. A protocol is a way of declaring that you support some methods. Adding a method to this list outside the protocol means that all "conforming" classes accidentally declare the new method even though they don't implement it. If some class implemented the NSObject protocol but did not descend from NSObject, and then you added a method to the protocol, that would break the class's conformance.
You can, however, create a new protocol that includes the old one with a declaration like #protocol SpecialObject <NSObject>.
I think you may be mixing up terms here and there. Extensions, Categories, Protocols, Interfaces and Classes are all different things in Objective-C. In The Objective-C 2.0 Language Apple describes the differences very well, including the benefits and drawbacks to using categories and extensions.
If you think about it, what is a "Category" or "Extension" in the conceptual sense? It's a way of adding functionality to a Class. In Objective-C, protocols are designed to have no implementation. Therefore, how would you add or extend the implementation of something that doesn't have implementation to begin with?
if you're already writing a category, why not just add in the protocol definition in the header right after the category definition?
i.e.
#interface NSString (MyCategory)
- (BOOL) startsWith: (NSString*) prefix;
#end
#protocol MyExtendedProtocolName <NSString>
//Method declarations go here
#end
this way any class that imports the category header will also get the protocol definition, and you can add it into your class..
#interface MyClass <OriginalProtocol,MyExtendedProtocolName>
also, be careful when subclassing NSString, it's a cluster and you may not always get the behaviour you're expecting.
Adam Sharp posted a solution that worked for me.
It involves 3 steps:
Defining the methods you want to add as #optional on a protocol.
Making the objects you want to extend conform to that protocol.
Copying those methods into those objects at runtime.
Check out the link for the full details.