Getting a string value for a property of a class with autocompletion feature and checking it at compile-time is very useful.
Get the property name for a class:
@interface AnyClass : NSObject
@property (strong) NSData *data;
@end
// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = @"data";
// == My approach ==
// C string for a class
keypathForClass(AnyClass, data); // ==> "data"
// NSString for a class
keypathStringForClass(AnyClass, data); // ==> @"data"
Get the property name for a protocol:
@protocol AnyProtocol
@property (strong) NSDate *date;
@end
// C string for a protocol
keypathForProtocol(AnyProtocol, date); // ==> "date"
// NSString for a protocol
keypathStringForProtocol(AnyProtocol, date); // ==> @"date"
Just simply add the following two files in your project:
NSObject+PropertyName.h:
#import <Foundation/Foundation.h>
/* Get property name for the class (C string or NSSting). */
#define keypathForClass(Klass, PropertyName) \
(((void)(NO && ((void)[Klass _nullObjectForCheckingPropertyName].PropertyName, NO)), # PropertyName))
#define keypathStringForClass(Klass, PropertyName) \
@keypathForClass(Klass, PropertyName)
/* Get property name for the protocol (C string or NSSting). */
#define keypathForProtocol(Protocol, PropertyName) \
(((void)(NO && ((void)((NSObject<Protocol> *)[NSObject _nullObjectForCheckingPropertyName]).PropertyName, NO)), # PropertyName))
#define keypathStringForProtocol(Protocol, PropertyName) \
@keypathForProtocol(Protocol, PropertyName)
@interface NSObject (PropertyName)
+ (instancetype)_nullObjectForCheckingPropertyName;
@end
NSObject+PropertyName.m:
#import "NSObject+PropertyName.h"
@implementation NSObject (PropertyName)
+ (instancetype)_nullObjectForCheckingPropertyName;{
return nil;
}
@end
Now you can get the property name by the macro and check the property at compile-time without any side effect.
This gist demonstrates a simple idea using null-object pattern and the trick of keypath(...)
derived from libextobjc and ReactiveCocoa to get the property name from a class.
Although the compiler should ignore ((void)(NO && ((void)<#_ObjC_Message_#>, NO))
statement at compile-time, the method +_nullObjectForCheckingPropertyName
still guarantees that the getter method in <#_ObjC_Message_#>
will not be invoked to prevent if from any possible side effect.