Is it okay to return a subclass from a class constructor that uses instancetype? - objective-c

I have a class method in a category to construct a Cocoa collection in some way that the built-in initializers don't allow. Due to the limited initializer functionality, I have to use the mutable version of the collection to actually build it. Here's an example for NS{Mutable}IndexSet:
#implementation NSIndexSet (WSSNonContiguous)
+ (instancetype)WSSIndexSetFromMask:(NSUInteger)mask
{
NSMutableIndexSet * set = [NSMutableIndexSet indexSet];
for( NSUInteger i = 0; i < (sizeof(NSUInteger) * 8); i++ ){
if( mask & (1l << i) ){
[set addIndex:i];
}
}
return set;
}
My return type is sometimes a lie here -- there's always a mutable collection being returned, whether the user is requesting an immutable version or not.
Is it still appropriate to use instancetype in cases like this, or should I go with id? If I do use instancetype, should I also be explicitly re-creating the collection:
// Ick?
return [[self alloc] initWithIndexSet:set];
to make sure an immutable copy is returned when the call is +[NSIndexSet WSSIndexSetFromMask:]?

Everything is okay:
NSIndexSet *set = [[NSIndexSet WSSIndexSetFromMask:0] addIndex:0];
No visible #interface for 'NSIndexSet' declares the selector 'addIndex:'
instancetype says to the sender, that you return a instance of the receivers type even it is a subtype. For the sender it is a NSIndexSet, because it is send to the class object of NSIndexSet.
An introspection that way, that someone looks to the return type and sees a subclass and takes any advantage out of this information, is malformed. The contract is build with the return type and this is in this case NSIndexSet.

Related

Obj-C: using mutable and returning non mutable classes in methods

In objective-C I find myself creating alot of Mutable objects and then returning them as non mutable objects. Is the way I am doing it here, simply returning the NSMutableSet as an NSSet a good practice? I was thinking maybe I should specify that i make a copy of it.
/** Returns all the names of the variables used in a given
* program. If non are used it returns nil */
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSMutableSet* variablesUsed = [[NSMutableSet alloc]init];
if ([program isKindOfClass:[NSArray class]]) {
for (NSString *str in program)
{
if ([str isEqual:#"x"] || [str isEqual:#"y"] || [str isEqual:#"a"] || [str isEqual:#"b"])
[variablesUsed addObject:str];
}
}
if ([variablesUsed count] > 0) {
return variablesUsed;
} else {
return nil;
}
}
If I were you, I would do it this way.
+ (NSSet *)variablesUsedInProgram:(id)program
{
NSSet *variablesUsed;
if ([program isKindOfClass:[NSArray class]]) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF = 'x' or SELF = 'y' or SELF = 'z'"];
variablesUsed = [NSSet setWithArray:[program filteredArrayUsingPredicate:predicate]];
}
int count;
return (count = [variablesUsed count]) > 0 ? variablesUsed : nil;
}
I find using predicate to filter array quite comprehensive and easy. Rather than dealing with creating a new mutable type and then testing certain condition, adding until the loop; in this scenario, it seems to be easier to use predicate. Hope this helps you.
It depends how much safety you require. If you return the object as an NSSet it will still be an NSMutableSet, so it could easily be cast back to one and modified.
Certainly, if you're creating a public API, I'd recommend returning a copy. For in internal project, perhaps the method signature already makes the intention clear enough.
Its, worth noting that, generally the performance impact of returning a copy is negligible - copying an immutable instance is effectively free whereas each copy sent to a mutable-passing-as-immutable will create another copy. So I would say its good practice to default to.
No. This is an absolutely correct OOP approach (it takes advantage of polymorphism). Every NSMutableSet is a proper NSSet. Don't copy superfluously.
Not a full answer here, consider NSProxy's one, but I want to clarify something.
In your case you create your object from scratch, and you don't set any ivar to point to that object. In my opinion in a good percentage of cases you don't need to make a copy of the mutable object returned. But if there is a good reason to deny the class client from mutating the class, then you should copy the variable.
Consider a property like this:
#property (nonatomic,assign) NSSet* set;
The class client could do this:
NSMutableSet* set= ... ; // inizialized to some value
classInstance.set= set;
// Mutate the set
Once mutated the set it could make the class be in an inconsistent state.
That's why when I have a property with the type of a class that has also a mutable version, I always put copy instead of assign in the property.

Objective-C - iVar Scoped Method Variables?

I was messing around in Objective-C earlier, and I ran into a quite common situation:
I had a class, which was not a singleton, that needed a variable shared between method calls, like static, but each instance needed it's own variable. However, this variable only needed to be used in one particular method, we'll call it -foo.
What I'd love to do, is have a macro, let's call it ivar, which lets me do the following:
#implementation MyClass
-(foo)
{
ivar int someVal = 10; // default value, ivar scoped variable.
}
-(bar)
{
someVal = 5; // error, outside of `foo`'s scope.
}
#end
How the variable is defined does not matter to me (either a macro like OBJC_IVAR(Type, Name, Default) or ivar someType someName = value), as long as it meets the following requirements:
Has thread safety
Can have variable of same name (but different value) in another method
Type-less (doesn't matter what type the variable is)
Default Value support
Variable can be declared in one line (I shouldn't have to write 15 lines of code just to put a variable in my code)
I am currently working on an Objective-C++ implementation myself, I was just wondering if anyone else had any thoughts (or existing tools) on how to do this.
Obviously, this doesn't have to be done with a true iVar. More likely, this should be done with associated objects at run-time, which also manages deallocation for us.
After a lot of time spent, I believe I have a fully working solution in Objective-C++. Some of the features:
The variables are unique. As long as they have a different scope, their values are independent
Each instance has it's own values
Thread safety (accomplished by associated objects)
Simple variable declaration:
Macro overloading: only specify the information that you need
Possible ways to define an OBJC_IVAR:
OBJC_IVAR(); // creates a warning, does nothing
OBJC_IVAR(Name); // creates an ivar named 'Name' of type 'id'
OBJC_IVAR(Type, Name); // creates an ivar named 'Name' of type 'Type'
OBJC_IVAR(Type, Name, Default); // creates an ivar named 'Name', of type 'Type', and a default value of 'Default' (which is only executed once);
Full Type Support with C++ templates (__weak, __strong, __autoreleasing, volatile, etc. are all supported)
Subclasses do not share variables with their superclasses (so no chance for conflicts, variables really are limited to their scope).
Can be used in singletons without issue
Is fast, takes ~15-30 CPU cycles to look up a variable, and once it's looked up, takes as long as any other variable to set it.
Most of the hard work is done by the pre-processor, which allows for faster code
Just drag-and-drop into an existing Xcode project, doesn't rely on a custom processor
Some minor cons to the implementation:
Objects must have an ownership specifier (limitation with C++ references: Reference to non-const type 'id' with no explicit ownership). Is easily fixed by adding __strong, __weak, or __autoreleasing to the type of the variable
Implementation is hard to read. Because it relies so much on C++ templates and Objective-C working together in harmony, it's difficult to just change 'one thing' and hope for it to work. I have added extensive comments to the implementation, so hopefully that frees some of the burden.
Method swizzling can confuse this majorly. Not the largest of issues, but if you start playing around with method swizzling, don't be surprised if you get unexpected results.
Cannot be used inside a C++ object. Unfortunately, C++ doesn't support runtime attributes, like objective-c does, so we cannot rely upon our variables being cleaned up eventually. For this reason, you cannot use OBJC_IVAR while inside a C++ object. I would be interested in seeing an implementation for that, though.
#line can mess this up drastically, so don't use it.
Version History
1.0: Initial Release
1.1: Updated OBJC_IVAR_NAME to rely only on the preprocessor. As a result, we cannot use __func__.
So, without further ado, here is the code:
OBJC_IVAR.hpp
//
// OBJC_IVAR.h
// TestProj
//
// Created by Richard Ross on 8/17/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#ifndef OBJC_IVAR_HPP
#define OBJC_IVAR_HPP
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "NSValue+CppObject.h"
// Argument counting algorithm. Not too complex
#define __NARG(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARG(...) __NARG(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
// Different implementations based on number of parameters passed in
#define __OBJC_IVAR(N, ...) _OBJC_IVAR_ ## N (__VA_ARGS__)
#define _OBJC_IVAR(N, ...) __OBJC_IVAR(N, __VA_ARGS__)
// Usage: OBJC_IVAR(Type (optional), Name (required), Default (optional))
#define OBJC_IVAR(...) _OBJC_IVAR(NARG(__VA_ARGS__), __VA_ARGS__)
// create a unique name. we use '__COUNTER__' here to support scoping on the same line, for compressed source code
#define __OBJC_IVAR_STRINGIFY_NAME(file, line, name, counter) #file ":" #line " " #name ":" #counter
#define _OBJC_IVAR_NAME(file, line, name, counter) __OBJC_IVAR_STRINGIFY_NAME(file, line, name, counter)
#define OBJC_IVAR_NAME(name) _OBJC_IVAR_NAME(__FILE__, __LINE__, name, __COUNTER__)
// old style creation. advantage: uses __func__ to determine calling function
// #define OBJC_IVAR_NAME(Name) [NSString stringWithFormat:#"%s:%i %s:%s:%i", __FILE__, __LINE__, __func__, #Name, __COUNTER__]
// implemenations for each of the overloads
#define _OBJC_IVAR_0(...) _Pragma("message \"Cannot call OBJC_IVAR with 0 params!\"")
#define _OBJC_IVAR_1(Name) _OBJC_IVAR_2(__strong id, Name)
// first major implemenation. because we do no assignment here, we don't have to check for is_set
#define _OBJC_IVAR_2(Type, Name) Type& Name = (_OBJC_IVAR::IMPL<Type>(self, OBJC_IVAR_NAME(Name)))
// this is where things get fun. we have 'OBJC_IVAR_CUR_NAME', instead of calling OBJC_IVAR_NAME
// multiple times, because we must ensure that COUNTER does not change during the course of the macro
// this is the 'inner bowels' of C, and it's quite hacky. Returns a reference to an associated object
// which is wrapped in a NSValue. Note that we only evaluate 'default' once throught the course of the
// application's cycle, so you can feel free to put intensive loading code there.
static NSString *_OBJC_IVAR_CUR_NAME;
#define _OBJC_IVAR_3(Type, Name, Default) Type& Name = (_OBJC_IVAR::IS_SET(self, (_OBJC_IVAR_CUR_NAME = OBJC_IVAR_NAME(Name))) ? _OBJC_IVAR::IMPL<Type>(self, _OBJC_IVAR_CUR_NAME) : _OBJC_IVAR::IMPL<Type>(self, _OBJC_IVAR_CUR_NAME, Default))
// namespace to wrap al lof our functions
namespace _OBJC_IVAR
{
// internal dictionary of all associated object names, so that we don't run
// into memory management issues. we use a set here, because we should never
// have duplicate associated object names.
static NSMutableSet *_names = [NSMutableSet set];
// wraps a value and a reference to a value. used over std::reference_wrapper,
// as that doesn't actually copy in the value passed. That is required for what
// we are doing, as we cannot be assigning to constants.
template<typename T>
class Wrapper {
private:
// private value wrapped by this object.
T _value;
// private reference wrapped by this object. should always point to _value.
T& _ref;
public:
// default constructor. assumes 'T' has a valid 0-argument constructor
Wrapper() : _value(), _ref(_value) { }
// argument constructor. makes sure that value is initialized properly
Wrapper(T val) : _value(val), _ref(_value) { }
// returns the reference wrapped by this object
operator T& () {
return _ref;
}
T& get() {
return _ref;
}
};
// interns a name. because objc_getAssociatedObject works only by comparing
// pointers (and +stringWithFormat: isn't guaranteed to return the same pointer),
// we have to make sure that we maintain a list of all valid associated object
// names. these are NOT linked to specific objects, which allows us to reuse some
// memory
inline NSString *name_intern(NSString *name)
{
// intern the value. first check if the object has been interned already,
// and if it is, return that interned value
if (id tmpName = [_names member:name])
{
name = tmpName;
}
// if we haven't interned this value before, then add it to the list and return it.
else
{
[_names addObject:name];
}
return name;
}
// check and see if the requested iVar has been set yet. used for default value setting
BOOL IS_SET(id target, NSString *name)
{
// first intern the name
name = name_intern(name);
// check if the object has this property. objc_getAssociatedObject will ALWAYS
// return NULL if the object doesn't exist. Note the bridged cast. This is because
// objc_getAssociatedObject doesn't care what you throw into the second parameter,
// as long as it is a pointer. That gives us the flexibility at a later date, to,
// for example, just pass a pointer to a single byte, and pull out the value that
// way. However, we pass in a NSString pointer, because it makes it easy for us to
// use and to re-use later.
id val = objc_getAssociatedObject(target, (__bridge const void *) name);
return val != nil;
}
// the actual implementation for setting the iVar. luckily this code isn't too hacky,
// but it is a bit confusing.
template<typename T>
Wrapper<T>& IMPL(id target, NSString *name)
{
// first intern the name
name = name_intern(name);
// define a reference. we use pointers & new here, because C++ memory managment is
// weird at best. Most of the time, you should be using RAII, but when dealing with
// templates & objective-c interpolation, it is almost required that you use pointers
// with new.
Wrapper<T> *reference = nullptr;
// check and see if the object already contains this property, if so, return that value
NSValue *result = objc_getAssociatedObject(target, (__bridge const void *) name);
if (result == nil)
{
// at this point, we need to create a new iVar, with the default constructor for the type.
// for objective-c objects this is 'nil', for integers and floating point values this is 0,
// for C++ structs and classes, this calls the default constructor. If one doesn't exist,
// you WILL get a compile error.
reference = new Wrapper<T>();
// we now set up the object that will hold this wrapper. This is an extension on NSValue
// which allows us to store a generic pointer (in this case a C++ object), and run desired
// code on -dealloc (which will be called at the time the parent object is destroyed), in
// this case, free the memory used by our wrapper.
result = [NSValue valueWithCppObject:reference onDealloc:^(void *) {
delete reference;
}];
// finally, set the associated object to the target, and now we are good to go.
// We use OBJC_ASSOCIATION_RETAIN, so that our NSValue is properly freed when done.
objc_setAssociatedObject(target, (__bridge const void *) name, result, OBJC_ASSOCIATION_RETAIN);
}
// from result, we cast it's -cppObjectValue to a Wrapper, to pull out the value.
reference = static_cast<Wrapper<T> *>([result cppObjectValue]);
// finally, return the pointer as a reference, not a pointer
return *reference;
}
// this is pretty much the same as the other IMPL, but it has specific code for default values.
// I will ignore everything that is the same about the two functions, and only focus on the
// differences, which are few, but mandatory.
template<typename T>
Wrapper<T>& IMPL(id target, NSString *name, const T& defVal)
{
name = name_intern(name);
Wrapper<T> *reference = nullptr; // asign to be the default constructor for 'T'
NSValue *result = objc_getAssociatedObject(target, (__bridge const void *) name);
if (result == nil)
{
// this is the only difference. Instead of constructing with the default constructor,
// simply pass in our new default value as a copy.
reference = new Wrapper<T>(defVal);
result = [NSValue valueWithCppObject:reference onDealloc:^(void *) {
delete reference;
}];
objc_setAssociatedObject(target, (__bridge const void *) name, result, OBJC_ASSOCIATION_RETAIN);
}
reference = static_cast<Wrapper<T> *>([result cppObjectValue]);
return *reference;
}
}
#endif // OBJC_IVAR_HPP
NSValue+CppObject.h
//
// NSValue+CppObject.h
// TestProj
//
// Created by Richard Ross on 8/17/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
// Extension on NSValue to add C++ object support. Because of the difficulty
// involved in templates, I took the easy way out and simply passed in a block
// of code to be run at dealloc.
#interface NSValue (CppObject)
// create a new NSValue instance that holds ptr, and calls 'deallocBlock' on destruction.
+(id) valueWithCppObject:(void *) ptr onDealloc:(void (^)(void *)) deallocBlock;
-(id) initWithCppObject:(void *) ptr onDealloc:(void (^)(void *)) deallocBlock;
// get the held pointer of this object. I called it -cppObjectValue, so
// there was no confusion with -pointerValue.
-(void *) cppObjectValue;
#end
NSValue+CppObject.m
//
// NSValue+CppObject.m
// TestProj
//
// Created by Richard Ross on 8/17/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import "NSValue+CppObject.h"
// the concrete NSValue subclass for supporting C++ objects. Pretty straight-forward interface.
#interface ConcreteCppObject : NSValue
{
// the underlying object that is being pointed to
void *_object;
// the block that is called on -dealloc
void (^_deallocBlock)(void *);
}
#end
#implementation ConcreteCppObject
// object initialization
+(id) valueWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[self alloc] initWithCppObject:ptr onDealloc:deallocBlock];
}
-(id) initWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
if (self = [super init])
{
_object = ptr;
_deallocBlock = deallocBlock;
}
return self;
}
// required methods for subclassing NSValue
-(const char *) objCType
{
return #encode(void *);
}
-(void) getValue:(void *)value
{
*((void **) value) = _object;
}
// comparison
-(BOOL) isEqual:(id)compare
{
if (![compare isKindOfClass:[self class]])
return NO;
return [compare cppObjectValue] == [self cppObjectValue];
}
// cleanup
-(void) dealloc
{
// this should manage cleanup for us
_deallocBlock(_object);
}
// value access
-(void *) cppObjectValue
{
return _object;
}
#end
// NSValue additions for creating the concrete instances
#implementation NSValue (CppObject)
// object initialization
+(id) valueWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[ConcreteCppObject alloc] initWithCppObject:ptr onDealloc:deallocBlock];
}
-(id) initWithCppObject:(void *)ptr onDealloc:(void (^)(void *))deallocBlock
{
return [[self class] valueWithCppObject:ptr onDealloc:deallocBlock];
}
// unless the NSValue IS a ConcreteCppObject, then we shouldn't do anything here
-(void *) cppObjectValue
{
[self doesNotRecognizeSelector:_cmd];
return nil;
}
#end
Example Usage:
#import "OBJC_IVAR.hpp"
#interface SomeObject : NSObject
-(void) doSomething;
#end
#implementation SomeObject
-(void) doSomething
{
OBJC_IVAR(__strong id, test, #"Hello World!");
OBJC_IVAR(int, test2, 15);
NSLog(#"%#", test);
NSLog(#"%i", test2 += 7);
// new scope
{
OBJC_IVAR(int, test, 100);
NSLog(#"%i", ++test);
}
[self somethingElse];
}
-(void) somethingElse
{
OBJC_IVAR(int, newVar, 7);
NSLog(#"%i", newVar++);
}
#end
int main()
{
SomeObject *obj = [SomeObject new];
[obj doSomething];
[obj doSomething];
[obj doSomething];
}
I had a class, which was not a singleton, that needed a variable
shared between method calls, like static, but each instance needed
it's own variable.
In that case, the variable is part of the object's state, and it's therefore most appropriate to use an instance variable (or a property). This is exactly what ivars are for, whether they're used in a dozen methods or just one.
I am currently working on an Objective-C++ implementation myself, I
was just wondering if anyone else had any thoughts (or existing tools)
on how to do this.
My advice is to not do it at all. If your goal is to avoid clutter, don't go needlessly trying to add a new storage class to the language.
However, if you're determined to pursue this line, I'd look at using blocks instead of associated objects. Blocks get their own copies of variables that are scoped to the lifetime of the block. For example, you can do this:
- (void)func
{
__block int i = 0;
void (^foo)() = ^{
i++;
NSLog(#"i = %d", i);
};
foo();
foo();
foo();
}
and the output you get is:
i = 1
i = 2
i = 3
Perhaps you can find a clever way to wrap that up in a macro, but it looks to me like a lot of trouble just to avoid declaring an instance variable.

Implementing NSFastEnumerator: EXC_BAD_ACCESS when iterating with for…in

I have a data structure that I wanted to enumerate. I tried to implement my object's NSFastEnumerator as follows:
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
objects:(__unsafe_unretained id [])buffer
count:(NSUInteger)len {
NSUInteger c = 0;
while (c < len) {
id obj = [self objectAtIndex:state->state];
if (obj == nil) break;
buffer[c] = obj;
c++;
state->state++;
}
state->itemsPtr = buffer;
state->mutationsPtr = nil;
return c;
}
If I use objectAtIndex directly, my object works properly. I get a nil when the index doesn't exist. But when I then use the for loop:
for (Pin *pin in coll) { ... }
the code runs through the above function fine and fills in state with what appears to be valid values and returns the number of objects, then I get an EXC_BAD_ACCESS failure at the for statement itself.
What am I doing wrong in this implementation?
I just had a similar issues, and after looking more closely into Apple's FastEnumerationSample, this part (that I had overlooked) jumped at me:
// We are not tracking mutations, so we'll set state->mutationsPtr to point into one of our extra values,
// since these values are not otherwise used by the protocol.
// If your class was mutable, you may choose to use an internal variable that is updated when the class is mutated.
// state->mutationsPtr MUST NOT be NULL.
state->mutationsPtr = &state->extra[0];
The important part being: state->mutationsPtr MUST NOT be NULL. I just used the example line provided and it worked like a charm!
I'm assuming you're using ARC. The problem may be that the buffer is an array of __unsafe_unretained objects, so ARC might be over-releasing them. But what does your objectAtIndex: method look like? This shouldn't be a problem if you are returning objects that are guaranteed to be alive at least as long as your object itself.
Instead of:
id obj = [self objectAtIndex:state->state];
use
__unsafe_unretained id = [self objectAtIndex:state->state];

Comparing objects in Obj-C

How does one compare objects in Objective-C?
Is it as simple as == ?
I want to check an array for an object and if it doesnt exist add it to the array otherwise, remove it from the array.
Comparing objects in Objective-C works much the same as in Java or other object-oriented languages:
== compares the object reference; in Objective-C, whether they occupy the same memory address.
isEqual:, a method defined on NSObject, checks whether two objects are "the same." You can override this method to provide your own equality checking for your objects.
So generally to do what you want, you would do:
if(![myArray containsObject:anObject]) {
[myArray addObject:anObject];
}
This works because the Objective-C array type, NSArray, has a method called containsObject: which sends the isEqual: message to every object it contains with your object as the argument. It does not use == unless the implementation of isEqual: relies on ==.
If you're working entirely with objects that you implement, remember you can override isEqual: to provide your own equality checking. Usually this is done by comparing fields of your objects.
Every Objective-C object has a method called isEqual:.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/isEqual:
So you would want to override this for your custom object types.
One particular important note in the documentation:
If two objects are equal, they must
have the same hash value. This last
point is particularly important if you
define isEqual: in a subclass and
intend to put instances of that
subclass into a collection. Make sure
you also define hash in your subclass.
== will compare the pointer, you need to override
- (BOOL)isEqual:(id)anObject
Implement isEqual: and hash
Per the Apple documentation on NSObject you need to implement isEqual: and hash at a minimum. Below you'll find one way to implement object equality, of course how to implement hash enters the land of serious debate here on StackOverflow, but this will work. General rule - you need to define what constitutes object equality and for each unique object they should have a unique hash. It is best practice to add an object specific equality method as well, for example NSString has isEqualToString:.
- (BOOL)isEqual:(id)object
{
BOOL result = NO;
if ([object isKindOfClass:[self class]]) {
CLPObject *otherObject = object;
result = [self.name isEqualToString:[otherObject name]] &&
[self.shortName isEqualToString:[otherObject shortName]] &&
[self.identifier isEqualToString:[otherObject identifier]] &&
self.boardingAllowed == [otherObject isBoardingAllowed];
}
return result;
}
- (NSUInteger)hash
{
NSUInteger result = 1;
NSUInteger prime = 31;
result = prime * result + [_name hash];
result = prime * result + [_shortName hash];
result = prime * result + [_identifier hash];
result = prime * result + _boardingAllowed;
return result;
}

How to build a NSArray (or NSMutableArray) of class methods in Objective-C?

I'm trying to build a NSArray of methods in Objective-C.
(What I'm trying to accomplish here is something like the following in C)
typedef (void)(*handler)(int command);
void handleCommandA(void) { ... }
void handleCommandB(void) { ... }
static const handler handler_table[10] = {
handleCommandA, handleCommandB, handleCommandC
};
I have to port this to Objective-C and I don't know how to
build an array of function pointers (in Objective-c world,
class methods) at compile-time.
In Objective-C I have the following.
- (void)handleCommandA { ... }
- (void)handleCommandB { ... }
/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ]; /* This doesn't seem to work. */
The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.
You thus have three options;
Use a regular C-type array
Fold the functions into an NSObject derived class that will call them.
Use a protocol.
The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;
NSValue* selCommandA = [NSValue valueWithPointer:#selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:#selector(handleCommandB:)];
NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];
When you have retrieved the correct entry from the array, to convert back you would do;
SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];
(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)
Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.
E.g.
// some header
#protocol CommandHandler
#required
-(void) handleCommandA;
-(void) handleCommandB;
#end
// some other header
#interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}
Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g
-(void) registerForCommands:(CommandHandler*)handler
Use NSValue.
For example:
NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];
then to access :
handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:
NSString *exampleString = [NSString stringWithString:#"Hello"];
SEL methodName = #selector(stringByAppendingString:);
// ^This is the selector. Note that it just represents the name of a
// message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:#" world!"];
What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector — just the method name — so you might need to store the receiver as well.)