How can I get a list (in the form of an NSArray or NSDictionary) of a given object properties in Objective-C?
Imagine the following scenario: I have defined a parent class which just extends NSObject, that holds an NSString, a BOOL and an NSData object as properties. Then I have several classes which extend this parent class, adding a lot of different properties each.
Is there any way I could implement an instance method on the parent class that goes through the whole object and returns, say, an NSArray of each of the (child) class properties as NSStrings that are not on the parent class, so I can later use these NSString for KVC?
I just managed to get the answer myself. By using the Obj-C Runtime Library, I had access to the properties the way I wanted:
- (void)myMethod {
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithCString:propName
encoding:[NSString defaultCStringEncoding]];
NSString *propertyType = [NSString stringWithCString:propType
encoding:[NSString defaultCStringEncoding]];
...
}
}
free(properties);
}
This required me to make a 'getPropertyType' C function, which is mainly taken from an Apple code sample (can't remember right now the exact source):
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T') {
if (strlen(attribute) <= 4) {
break;
}
return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
}
}
return "#";
}
#boliva's answer is good, but needs a little extra to handle primitives, like int, long, float, double, etc.
I built off of his to add this functionality.
// PropertyUtil.h
#import
#interface PropertyUtil : NSObject
+ (NSDictionary *)classPropsFor:(Class)klass;
#end
// PropertyUtil.m
#import "PropertyUtil.h"
#import "objc/runtime.h"
#implementation PropertyUtil
static const char * getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// it's a C primitive type:
/*
if you want a list of what will be returned for these primitives, search online for
"objective-c" "Property Attribute Description Examples"
apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
*/
return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// it's another ObjC object type:
return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
}
}
return "";
}
+ (NSDictionary *)classPropsFor:(Class)klass
{
if (klass == NULL) {
return nil;
}
NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary dictionaryWithDictionary:results];
}
#end
#orange80's answer has one problem: It actually doesn't always terminate the string with 0s. This can lead to unexpected results like crashing while trying to convert it to UTF8 (I actually had a pretty annoying crashbug just because of that. Was fun debugging it ^^). I fixed it by actually getting an NSString from the attribute and then calling cStringUsingEncoding:. This works like a charm now. (Also works with ARC, at least for me)
So this is my version of the code now:
// PropertyUtil.h
#import
#interface PropertyUtil : NSObject
+ (NSDictionary *)classPropsFor:(Class)klass;
#end
// PropertyUtil.m
#import "PropertyUtil.h"
#import <objc/runtime.h>
#implementation PropertyUtil
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
//printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// it's a C primitive type:
/*
if you want a list of what will be returned for these primitives, search online for
"objective-c" "Property Attribute Description Examples"
apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// it's another ObjC object type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
+ (NSDictionary *)classPropsFor:(Class)klass
{
if (klass == NULL) {
return nil;
}
NSMutableDictionary *results = [[NSMutableDictionary alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary dictionaryWithDictionary:results];
}
#end
When I tried with iOS 3.2, the getPropertyType function doesn't work well with the property description. I found an example from iOS documentation: "Objective-C Runtime Programming Guide: Declared Properties".
Here is a revised code for property listing in iOS 3.2:
#import <objc/runtime.h>
#import <Foundation/Foundation.h>
...
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([UITouch class], &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
free(properties);
I've found that boliva's solution works fine in the simulator, but on device the fixed length substring causes problems. I have written a more Objective-C-friendly solution to this problem that works on the device. In my version, I convert the C-String of the attributes to an NSString and perform string operations on it to get a substring of just the type description.
/*
* #returns A string describing the type of the property
*/
+ (NSString *)propertyTypeStringOfProperty:(objc_property_t) property {
const char *attr = property_getAttributes(property);
NSString *const attributes = [NSString stringWithCString:attr encoding:NSUTF8StringEncoding];
NSRange const typeRangeStart = [attributes rangeOfString:#"T#\""]; // start of type string
if (typeRangeStart.location != NSNotFound) {
NSString *const typeStringWithQuote = [attributes substringFromIndex:typeRangeStart.location + typeRangeStart.length];
NSRange const typeRangeEnd = [typeStringWithQuote rangeOfString:#"\""]; // end of type string
if (typeRangeEnd.location != NSNotFound) {
NSString *const typeString = [typeStringWithQuote substringToIndex:typeRangeEnd.location];
return typeString;
}
}
return nil;
}
/**
* #returns (NSString) Dictionary of property name --> type
*/
+ (NSDictionary *)propertyTypeDictionaryOfClass:(Class)klass {
NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for(i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
NSString *propertyType = [self propertyTypeStringOfProperty:property];
[propertyMap setValue:propertyType forKey:propertyName];
}
}
free(properties);
return propertyMap;
}
This implementation works with both Objective-C object types and C primitives. It is iOS 8 compatible. This class provides three class methods:
+ (NSDictionary *) propertiesOfObject:(id)object;
Returns a dictionary of all visible properties of an object, including those from all its superclasses.
+ (NSDictionary *) propertiesOfClass:(Class)class;
Returns a dictionary of all visible properties of a class, including those from all its superclasses.
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Returns a dictionary of all visible properties that are specific to a subclass. Properties for its superclasses are not included.
One useful example of the use of these methods is to copy an object to a subclass instance in Objective-C without having to specify the properties in a copy method. Parts of this answer are based on the other answers to this question but it provides a cleaner interface to the desired functionality.
Header:
// SYNUtilities.h
#import <Foundation/Foundation.h>
#interface SYNUtilities : NSObject
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
#end
Implementation:
// SYNUtilities.m
#import "SYNUtilities.h"
#import <objc/objc-runtime.h>
#implementation SYNUtilities
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
NSMutableDictionary * properties = [NSMutableDictionary dictionary];
[self propertiesForHierarchyOfClass:class onDictionary:properties];
return [NSDictionary dictionaryWithDictionary:properties];
}
+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
if (class == NULL) {
return nil;
}
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
return [self propertiesForSubclass:class onDictionary:properties];
}
+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
if (class == NULL) {
return nil;
}
if (class == [NSObject class]) {
// On reaching the NSObject base class, return all properties collected.
return properties;
}
// Collect properties from the current class.
[self propertiesForSubclass:class onDictionary:properties];
// Collect properties from the superclass.
return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}
+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
unsigned int outCount, i;
objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = objcProperties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[properties setObject:propertyType forKey:propertyName];
}
}
free(objcProperties);
return properties;
}
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// A C primitive type:
/*
For example, int "i", long "l", unsigned "I", struct.
Apple docs list plenty of examples of values returned. For a list
of what will be returned for these primitives, search online for
"Objective-c" "Property Attribute Description Examples"
*/
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// An Objective C id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// Another Objective C id type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
#end
If someone is in the need of getting as well the properties inherited from the parent classes (as I did) here is some modification on "orange80" code to make it recursive:
+ (NSDictionary *)classPropsForClassHierarchy:(Class)klass onDictionary:(NSMutableDictionary *)results
{
if (klass == NULL) {
return nil;
}
//stop if we reach the NSObject class as is the base class
if (klass == [NSObject class]) {
return [NSDictionary dictionaryWithDictionary:results];
}
else{
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
//go for the superclass
return [PropertyUtil classPropsForClassHierarchy:[klass superclass] onDictionary:results];
}
}
The word "attributes" is a little fuzzy. Do you mean instance variables, properties, methods that look like accessors?
The answer to all three is "yes, but it's not very easy." The Objective-C runtime API includes functions to get the ivar list, method list or property list for a class (e.g., class_copyPropertyList()), and then a corresponding function for each type to get the name of an item in the list (e.g., property_getName()).
All in all, it can be kind of a lot of work to get it right, or at least a lot more than most people would want to do for what usually amounts to a really trivial feature.
Alternatively, you could just write a Ruby/Python script that just reads a header file and looks for whatever you'd consider "attributes" for the class.
I was able to get #orange80's answer to work WITH ARC ENABLED… ... for what I wanted - at least... but not without a bit of trial and error. Hopefully this additional information may spare someone the grief.
Save those classes he describes in his answer = as a class, and in your AppDelegate.h (or whatever), put #import PropertyUtil.h. Then in your...
- (void)applicationDidFinishLaunching:
(NSNotification *)aNotification {
method (or whatever)
…
PropertyUtil *props = [PropertyUtil new];
NSDictionary *propsD = [PropertyUtil classPropsFor:
(NSObject*)[gist class]];
NSLog(#"%#, %#", props, propsD);
…
The secret is to cast the instance variable of your class (in this Case my class is Gist, and my instance of Gist is gist) that you want to query... to NSObject… (id), etc, won't cut it.. for various, weird, esoteric reasons. This will give you some output like so…
<PropertyUtil: 0x7ff0ea92fd90>, {
apiURL = NSURL;
createdAt = NSDate;
files = NSArray;
gistDescription = NSString;
gistId = NSString;
gitPullURL = NSURL;
gitPushURL = NSURL;
htmlURL = NSURL;
isFork = c;
isPublic = c;
numberOfComments = Q;
updatedAt = NSDate;
userLogin = NSString;
}
For all of Apple's unabashed / OCD bragging about ObjC's "amazeballs" "introspection... They sure don't make it very easy to perform this simple "look" "at one's self", "so to speak"..
If you really want to go hog wild though.. check out.. class-dump, which is a mind-bogglingly insane way to peek into class headers of ANY executable, etc… It provides a VERBOSE look into your classes… that I, personally, find truly helpful - in many, many circumstances. it is actually why I i started seeking a solution to the OP's question. here are some of the usage parameters.. enjoy!
-a show instance variable offsets
-A show implementation addresses
--arch <arch> choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64)
-C <regex> only display classes matching regular expression
-f <str> find string in method name
-I sort classes, categories, and protocols by inheritance (overrides -s)
-r recursively expand frameworks and fixed VM shared libraries
-s sort classes and categories by name
-S sort methods by name
You have three magic spells
Ivar* ivars = class_copyIvarList(clazz, &count); // to get all iVars
objc_property_t *properties = class_copyPropertyList(clazz, &count); //to get all properties of a class
Method* methods = class_copyMethodList(clazz, &count); // to get all methods of a class.
Following piece of code can help you.
-(void) displayClassInfo
{
Class clazz = [self class];
u_int count;
Ivar* ivars = class_copyIvarList(clazz, &count);
NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
const char* ivarName = ivar_getName(ivars[i]);
ivarArray addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
}
free(ivars);
objc_property_t* properties = class_copyPropertyList(clazz, &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
const char* propertyName = property_getName(properties[i]);
[propertyArray addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
free(properties);
Method* methods = class_copyMethodList(clazz, &count);
NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
SEL selector = method_getName(methods[i]);
const char* methodName = sel_getName(selector);
[methodArray addObject:[NSString stringWithCString:methodName encoding:NSUTF8StringEncoding]];
}
free(methods);
NSDictionary* classInfo = [NSDictionary dictionaryWithObjectsAndKeys:
ivarArray, #"ivars",
propertyArray, #"properties",
methodArray, #"methods",
nil];
NSLog(#"%#", classInfo);
}
I was using function boliva provided, but apparently it stopped working with iOS 7. So now instead of static const char *getPropertyType(objc_property_t property) one can just use the following:
- (NSString*) classOfProperty:(NSString*)propName{
objc_property_t prop = class_getProperty([self class], [propName UTF8String]);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
NSString *class=[attrArray objectAtIndex:0];
return [[class stringByReplacingOccurrencesOfString:#"\"" withString:#""] stringByReplacingOccurrencesOfString:#"T#" withString:#""];
}
For Swift onlookers, you can get this functionality by utilising the Encodable functionality. I will explain how:
Conform your object to Encodable protocol
class ExampleObj: NSObject, Encodable {
var prop1: String = ""
var prop2: String = ""
}
Create extension for Encodable to provide toDictionary functionality
public func toDictionary() -> [String: AnyObject]? {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let data = try? encoder.encode(self),
let json = try? JSONSerialization.jsonObject(with: data, options: .init(rawValue: 0)), let jsonDict = json as? [String: AnyObject] else {
return nil
}
return jsonDict
}
Call toDictionary on your object instance and access keys property.
let exampleObj = ExampleObj()
exampleObj.toDictionary()?.keys
Voila! Access your properties like so:
for k in exampleObj!.keys {
print(k)
}
// Prints "prop1"
// Prints "prop2"
These answers are helpful, but I require more from that. All I want to do is to check whether the class type of a property is equal to that of an existing object. All the codes above are not capable of doing so, because:
To get class name of an object, object_getClassName() returns texts like these:
__NSArrayI (for an NSArray instance)
__NSArrayM (for an NSMutableArray instance)
__NSCFBoolean (an NSNumber object initialized by initWithBool:)
__NSCFNumber (an NSValue object initialized by [NSNumber initWithBool:])
But if invoking getPropertyType(...) from above sample code, wit 4 objc_property_t structs of properties of a class defined like this:
#property (nonatomic, strong) NSArray* a0;
#property (nonatomic, strong) NSArray* a1;
#property (nonatomic, copy) NSNumber* n0;
#property (nonatomic, copy) NSValue* n1;
it returns strings respectively as following:
NSArray
NSArray
NSNumber
NSValue
So it is not able to determine whether an NSObject is capable of being the value of one property of the class. How to do that then?
Here is my full sample code(function getPropertyType(...) is the same as above):
#import <objc/runtime.h>
#interface FOO : NSObject
#property (nonatomic, strong) NSArray* a0;
#property (nonatomic, strong) NSArray* a1;
#property (nonatomic, copy) NSNumber* n0;
#property (nonatomic, copy) NSValue* n1;
#end
#implementation FOO
#synthesize a0;
#synthesize a1;
#synthesize n0;
#synthesize n1;
#end
static const char *getPropertyType(objc_property_t property) {
const char *attributes = property_getAttributes(property);
//printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// it's a C primitive type:
// if you want a list of what will be returned for these primitives, search online for
// "objective-c" "Property Attribute Description Examples"
// apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// it's another ObjC object type:
NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
}
}
return "";
}
int main(int argc, char * argv[]) {
NSArray* a0 = [[NSArray alloc] init];
NSMutableArray* a1 = [[NSMutableArray alloc] init];
NSNumber* n0 = [[NSNumber alloc] initWithBool:YES];
NSValue* n1 = [[NSNumber alloc] initWithBool:NO];
const char* type0 = object_getClassName(a0);
const char* type1 = object_getClassName(a1);
const char* type2 = object_getClassName(n0);
const char* type3 = object_getClassName(n1);
objc_property_t property0 = class_getProperty(FOO.class, "a0");
objc_property_t property1 = class_getProperty(FOO.class, "a1");
objc_property_t property2 = class_getProperty(FOO.class, "n0");
objc_property_t property3 = class_getProperty(FOO.class, "n1");
const char * memberthype0 = getPropertyType(property0);//property_getAttributes(property0);
const char * memberthype1 = getPropertyType(property1);//property_getAttributes(property1);
const char * memberthype2 = getPropertyType(property2);//property_getAttributes(property0);
const char * memberthype3 = getPropertyType(property3);//property_getAttributes(property1);
NSLog(#"%s", type0);
NSLog(#"%s", type1);
NSLog(#"%s", type2);
NSLog(#"%s", type3);
NSLog(#"%s", memberthype0);
NSLog(#"%s", memberthype1);
NSLog(#"%s", memberthype2);
NSLog(#"%s", memberthype3);
return 0;
}
Related
In the code snippet below you can find my current implementantion where the function return an array with data. I'm looking for a way to return an NSDictionary instead with "IPv4", "IPv6" keys and values correspondingly.
+ (NSArray<NSString *> *) addressesFromService:(NSNetService *)service
{
// NSMutableArray<NSString *> *addresses = [[NSMutableArray alloc] init];
NSMutableDictionary<NSString *, NSString *> *result = [NSMutableDictionary dictionary];
char addressBuffer[INET6_ADDRSTRLEN];
for (NSData *data in service.addresses) {
memset(addressBuffer, 0, INET6_ADDRSTRLEN);
typedef union {
struct sockaddr sa;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
} ip_socket_address;
ip_socket_address *socketAddress = (ip_socket_address *)[data bytes];
if (socketAddress && (socketAddress->sa.sa_family == AF_INET || socketAddress->sa.sa_family == AF_INET6)) {
const char *addressStr = inet_ntop(
socketAddress->sa.sa_family,
(socketAddress->sa.sa_family == AF_INET ? (void *)&(socketAddress->ipv4.sin_addr) : (void *)&(socketAddress->ipv6.sin6_addr)),
addressBuffer,
sizeof(addressBuffer)
);
// if (addressStr) {
// NSString *address = [NSString stringWithUTF8String:addressStr];
// [addresses addObject:address];
// }
if (addressStr) {
NSString *key = AF_INET ? #"IPv4" : #"IPv6";
result[key] = [NSString stringWithUTF8String:addressStr];
}
}
}
// return [NSArray arrayWithArray:addresses];
return [result copy];
}
You'll need to adjust the method/function signature to match the return type (NSDictionary * instead of NSArray *) but for the snippet you provided you merely need to introduce a mutable dictionary right prior to the loop body:
NSMutableDictionary<NSString *, NSString *> *result = [NSMutableDictionary dictionary];
for (NSData *data in service.addresses) {
...
And then replace the part where you add values like this:
if (addressStr) {
NSString *key = socketAddress->sa.sa_family == AF_INET ? #"IPv4" : #"IPv6";
result[key] = [NSString stringWithUTF8String:addressStr];
}
If you want to return an immutable version of the dictionary, don't forget to make a copy:
return [result copy];
Is it possible to activate (bring to the fore) a window based on the values returned from CGWindowListCopyWindowInfo? (i.e Using the window ID (kCGWindowNumber) or something else.)
Edit:
I should specify that my app (which would run with accessibility permissions) needs to be able to do this for windows of other apps.
Since posting the question I've discovered AXUIElementPerformAction. Am I going in the right direction with this?
Or is running AppleScript bridge within my code the best approach?
You can attach to a process by pid and get its windows. Then use kAXRaiseAction to bring them to front, like this:
AXUIElementRef element = AXUIElementCreateApplication(pid);
if (element) {
CFArrayRef array;
AXUIElementCopyAttributeValues(element, kAXWindowsAttribute, 0, 99999, &array);
if (array == nullptr)
return;
NSArray *windows = (NSArray *)CFBridgingRelease(array);
for (NSUInteger i = 0; i < windows.count; ++i) {
AXUIElementRef ref = (__bridge AXUIElementRef)(windows[i]);
AXError error = AXUIElementPerformAction(ref, kAXRaiseAction);
// handle error
}
}
CFRelease(element);
No need to release array or windows. Children in arrays are handled automatically and the array is bridged to an NSArray which is released by ARC.
My answer's a little overcomplicated compared to what was already shared by Mike Lischke, but I've already posted it on a different SO question and I think it is a tiny bit closer to what you need:
#import <Cocoa/Cocoa.h>
#import <libproc.h>
#import <string.h>
#import <stdlib.h>
#import <stdio.h>
bool activate_window_of_id(unsigned long wid) {
bool success = false;
const CGWindowLevel kScreensaverWindowLevel = CGWindowLevelForKey(kCGScreenSaverWindowLevelKey);
CFArrayRef windowArray = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
CFIndex windowCount = 0;
if ((windowCount = CFArrayGetCount(windowArray))) {
for (CFIndex i = 0; i < windowCount; i++) {
NSDictionary *windowInfoDictionary = (__bridge NSDictionary *)((CFDictionaryRef)CFArrayGetValueAtIndex(windowArray, i));
NSNumber *ownerPID = (NSNumber *)(windowInfoDictionary[(id)kCGWindowOwnerPID]);
NSNumber *level = (NSNumber *)(windowInfoDictionary[(id)kCGWindowLayer]);
if (level.integerValue < kScreensaverWindowLevel) {
NSNumber *windowID = windowInfoDictionary[(id)kCGWindowNumber];
if (wid == windowID.integerValue) {
CFIndex appCount = [[[NSWorkspace sharedWorkspace] runningApplications] count];
for (CFIndex j = 0; j < appCount; j++) {
if (ownerPID.integerValue == [[[[NSWorkspace sharedWorkspace] runningApplications] objectAtIndex:j] processIdentifier]) {
NSRunningApplication *appWithPID = [[[NSWorkspace sharedWorkspace] runningApplications] objectAtIndex:j];
[appWithPID activateWithOptions:NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps];
char buf[PROC_PIDPATHINFO_MAXSIZE];
proc_pidpath(ownerPID.integerValue, buf, sizeof(buf));
NSString *buffer = [NSString stringWithUTF8String:buf];
unsigned long location = [buffer rangeOfString:#".app/Contents/MacOS/" options:NSBackwardsSearch].location;
NSString *path = (location != NSNotFound) ? [buffer substringWithRange:NSMakeRange(0, location)] : buffer;
NSString *app = [#" of application \\\"" stringByAppendingString:[path lastPathComponent]];
NSString *index = [#"set index of window id " stringByAppendingString:[windowID stringValue]];
NSString *execScript = [[index stringByAppendingString:app] stringByAppendingString:#"\\\" to 1"];
char *pointer = NULL;
size_t buffer_size = 0;
NSMutableArray *array = [[NSMutableArray alloc] init];
FILE *file = popen([[[#"osascript -e \"" stringByAppendingString:execScript] stringByAppendingString:#"\" 2>&1"] UTF8String], "r");
while (getline(&pointer, &buffer_size, file) != -1)
[array addObject:[NSString stringWithUTF8String:pointer]];
char *error = (char *)[[array componentsJoinedByString:#""] UTF8String];
if (strlen(error) > 0 && error[strlen(error) - 1] == '\n')
error[strlen(error) - 1] = '\0';
if ([[NSString stringWithUTF8String:error] isEqualToString:#""])
success = true;
[array release];
free(pointer);
pclose(file);
break;
}
}
}
}
}
}
CFRelease(windowArray);
return success;
}
The code it is based on does not work as advertised for its original purpose. Although, it did help me a lot to get working all the stuff I needed to answer this question. The code my answer is based on can be found here.
I was just wondering if there is a quick and easy way of printing out to the log all of the various values of the properties to my class for debugging purposes. Like I would like to know what the values of all of the BOOLs, floats, etc. are.
This question seems the have the answer to your question.
Update:
I got curious and made a catagory:
//Using Xcode 4.5.2 - iOS 6 - LLDB - Automatic Reference Counting
//NSObject+logProperties.h
#interface NSObject (logProperties)
- (void) logProperties;
#end
//NSObject+logProperties.m
#import "NSObject+logProperties.h"
#import <objc/runtime.h>
#implementation NSObject (logProperties)
- (void) logProperties {
NSLog(#"----------------------------------------------- Properties for object %#", self);
#autoreleasepool {
unsigned int numberOfProperties = 0;
objc_property_t *propertyArray = class_copyPropertyList([self class], &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++) {
objc_property_t property = propertyArray[i];
NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)];
NSLog(#"Property %# Value: %#", name, [self valueForKey:name]);
}
free(propertyArray);
}
NSLog(#"-----------------------------------------------");
}
#end
Include it in your class: #import "NSObject+logProperties.h"
and call [self logProperties]; to those properties!
The current answers just show how to do it for properties. If you want every instance variable printed out you could do something like the below.
- (void)logAllProperties {
unsigned int count;
Ivar *ivars = class_copyIvarList([self class], &count);
for (unsigned int i = 0; i < count; i++) {
Ivar ivar = ivars[i];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
ptrdiff_t offset = ivar_getOffset(ivar);
if (strncmp(type, "i", 1) == 0) {
int intValue = *(int*)((uintptr_t)self + offset);
NSLog(#"%s = %i", name, intValue);
} else if (strncmp(type, "f", 1) == 0) {
float floatValue = *(float*)((uintptr_t)self + offset);
NSLog(#"%s = %f", name, floatValue);
} else if (strncmp(type, "#", 1) == 0) {
id value = object_getIvar(self, ivar);
NSLog(#"%s = %#", name, value);
}
// And the rest for other type encodings
}
free(ivars);
}
Although I wouldn't particularly suggest doing this in practice, but if it's for debug purposes then that's fine. You could implement this as a category on NSObject and keep it lying around for use when debugging. If completed for all type encodings then it could make for a very nice little method.
There are now these methods on NSObject :
#interface NSObject (Private)
-(id)_ivarDescription;
-(id)_shortMethodDescription;
-(id)_methodDescription;
#end
In swift:
myObject.perform("_ivarDescription")
Thanks to this article
yes, one way would be to ask for all properties and then use KVC for example:
//properties
unsigned int cProperties = 0;
objc_property_t *props = class_copyPropertyList(self.class, &cProperties);
for(int i = 0; i < cProperties; i++) {
const char *name = property_getName(props[i]);
NSLog(#"%#=%#", name, [self valueForKey:name];
}
an alternate way is to go through all the methods of a class, get the return type, invoke and print it
The quick and dirty would be to override debugDescription:
-(NSString*)debugDescription {
NSString *str = [[NSString alloc] initWithFormat:#"My BOOL 1: %d, My Float: %f", self.myBool, self.myFoat];
return str;
}
Of course, if your object is complex, this could be time consuming.
I need to call the setter using the reflection. to do that i need selector. but the setterName var is nil in my code. I have setter and getter in my class. This is my code
objc_property_t *allProperties = class_copyPropertyList([object class], &allPropertyCount);
objc_property_t prop = class_getProperty(cls, propName);
for (unsigned int i = 0; i < allPropertyCount; i++) {
prop = allProperties[i];
}
char *setterName = property_copyAttributeValue(prop, "S");
SEL selector = NSSelectorFromString(setterName);
if([object respondsToSelector:#selector(selector)]){
return selector;
}
return nil;
The "S" and "G" attributes are only set if the property defines a custom setter or getter, respectively. Your code needs to check if setterName is NULL and, if so, generate the setter name from the property name.
A bit late in replying, hope this is useful to someone!
I have ARC enabled and I'm using the generated getters/setters.
// NSObject+Properties.m
#import "NSObject+Properties.h"
#import "objc/runtime.h"
#implementation NSObject (Properties)
static const char * getPropertyType(objc_property_t property) {
//Borrowed from http://stackoverflow.com/questions/754824/get-an-object-attributes-list-in-objective-c
//Many contributors to final solution
const char *attributes = property_getAttributes(property);
printf("attributes=%s\n", attributes);
char buffer[1 + strlen(attributes)];
strcpy(buffer, attributes);
char *state = buffer, *attribute;
while ((attribute = strsep(&state, ",")) != NULL) {
if (attribute[0] == 'T' && attribute[1] != '#') {
// it's a C primitive type:
/*
if you want a list of what will be returned for these primitives, search online for
"objective-c" "Property Attribute Description Examples"
apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
*/
return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
}
else if (attribute[0] == 'T' && attribute[1] == '#' && strlen(attribute) == 2) {
// it's an ObjC id type:
return "id";
}
else if (attribute[0] == 'T' && attribute[1] == '#') {
// it's another ObjC object type:
return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
}
}
return "";
}
- (NSDictionary *) getClassProperties
{
//Borrowed from http://stackoverflow.com/questions/754824/get-an-object-attributes-list-in-objective-c
//Many contributors to final solution
if (self == NULL || self == nil) {
return nil;
}
NSMutableDictionary *results = [[NSMutableDictionary alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([self class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
const char *propType = getPropertyType(property);
NSString *propertyName = [NSString stringWithUTF8String:propName];
NSString *propertyType = [NSString stringWithUTF8String:propType];
[results setObject:propertyType forKey:propertyName];
}
}
free(properties);
// returning a copy here to make sure the dictionary is immutable
return [NSDictionary dictionaryWithDictionary:results];
}
-(void) setProperty:(NSString *) propertyName value:(id) value {
SEL selector = [self buildGenericSetterForPropertyName:propertyName];
if( selector != NULL ) {
//The class responds to the selector
NSMethodSignature *aSignature = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:selector];
[anInvocation setTarget:self];
[anInvocation setArgument:&value atIndex:2];
[anInvocation invoke];
}
else {
//We don't know what the setter is, so directly access the iVar
Ivar iVar = class_getInstanceVariable([self class],[propertyName UTF8String]);
object_setIvar(self, iVar,value);
}
}
-(SEL) buildGenericSetterForPropertyName:(NSString *) name {
//Borrowed from https://github.com/AlanQuatermain/aqtoolkit/blob/master/Extensions/NSObject%2BProperties.m
NSMutableString * str = [NSMutableString stringWithString: #"set"];
[str appendString: [[name substringToIndex: 1] uppercaseString]];
if ( [name length] > 1 )
[str appendString: [name substringFromIndex: 1]];
//addition - the setter will accept one argument
[str appendString:#":"];
if( [[self class] instancesRespondToSelector:NSSelectorFromString(str)]) {
return NSSelectorFromString(str);
}
else {
return NULL;
}
}
#end
I'm trying to get a list of all the properties of an unknown class and the class of every property. By the moment I get a list of all the properties of an object(I do it recursively to get all of the superclasses). I inspired in this post
+ (NSArray *)classPropsFor:(Class)klass
{
NSLog(#"Properties for class:%#", klass);
if (klass == NULL || klass == [NSObject class]) {
return nil;
}
NSMutableArray *results = [[NSMutableArray alloc] init];
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList(klass, &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName) {
NSString *propertyName = [NSString stringWithUTF8String:propName];
[results addObject:propertyName];
}
NSArray* dict = [self classPropsFor:[klass superclass]];
[results addObjectsFromArray:dict];
}
free(properties);
return [NSArray arrayWithArray:results];
}
So now I want the class of every property and I do:
NSArray* properties = [PropertyUtil classPropsFor:[self class]];
for (NSString* property in properties) {
id value= [self valueForKey:property];
NSLog(#"Value class for key: %# is %#", property, [value class]);
}
The problem is it works for NSStrings or but not for custom classes, for that it returns me null. I want to recursively create a dictionary that represents an object that can have other objects inside and as I thinks I need to know the class of every property, is that possible?
Just made a tiny method for this.
// Simple as.
Class propertyClass = [customObject classOfPropertyNamed:propertyName];
Could be optimized in many ways, but I love it.
Implementation goes like:
-(Class)classOfPropertyNamed:(NSString*) propertyName
{
// Get Class of property to be populated.
Class propertyClass = nil;
objc_property_t property = class_getProperty([self class], [propertyName UTF8String]);
NSString *propertyAttributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:#","];
if (splitPropertyAttributes.count > 0)
{
// xcdoc://ios//library/prerelease/ios/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
NSString *encodeType = splitPropertyAttributes[0];
NSArray *splitEncodeType = [encodeType componentsSeparatedByString:#"\""];
NSString *className = splitEncodeType[1];
propertyClass = NSClassFromString(className);
}
return propertyClass;
}
It is part of eppz!kit, within a developing object representer called NSObject+EPPZRepresentable.h. It actually does what you are to achieve originally.
// Works vica-versa.
NSDictionary *representation = [customObject dictionaryRepresentation];
CustomClass = [CustomClass representableWithDictionaryRepresentation:representation];
It encodes many types, iterate trough collections, represents CoreGraphics types, UIColors, also represent / reconstruct object references.
New version spits you back even C type names and named struct types as well:
NSLog(#"%#", [self typeOfPropertyNamed:#"index"]); // unsigned int
NSLog(#"%#", [self typeOfPropertyNamed:#"area"]); // CGRect
NSLog(#"%#", [self typeOfPropertyNamed:#"keyColor"]); // UIColor
Part of eppz!model, feel free to use method implementations at https://github.com/eppz/eppz.model/blob/master/eppz!model/NSObject%2BEPPZModel_inspecting.m#L111
You should probably store the class (as a string) for each property at the same time as you store the propertyName. Maybe as a dictionary with property name as the key and class name as the value, or vice versa.
To get the class name, you can do something like this (put this right after you declare propertyName):
NSString* propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)];
NSArray* splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:#"\""];
if ([splitPropertyAttributes count] >= 2)
{
NSLog(#"Class of property: %#", [splitPropertyAttributes objectAtIndex:1]);
}
The string handling code is because the attributes include a number of pieces of information - the exact details are specified here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
UPDATED
This doesn't work for values that are nil. Instead you should use the runtime C API to obtain the class from the corresponding ivar or accessor method.
The following added to an NSObject category does the trick.
- (Class) classForKeyPath:(NSString*)keyPath {
Class class = 0;
unsigned int n = 0;
objc_property_t* properties = class_copyPropertyList(self.class, &n);
for (unsigned int i=0; i<n; i++) {
objc_property_t* property = properties + i;
NSString* name = [NSString stringWithCString:property_getName(*property) encoding:NSUTF8StringEncoding];
if (![keyPath isEqualToString:name]) continue;
const char* attributes = property_getAttributes(*property);
if (attributes[1] == '#') {
NSMutableString* className = [NSMutableString new];
for (int j=3; attributes[j] && attributes[j]!='"'; j++)
[className appendFormat:#"%c", attributes[j]];
class = NSClassFromString(className);
}
break;
}
free(properties);
return class;
}