I have many bean/data classes in my code which I'm using to convert to JSON for network communication purposes. The issue is, if there's a NSString property in my class I want to set its default value to empty string #"" rather than nil. One option I have is this :Setting Default Values For NSString Properties but I have to write code to set properties values, I don't want to do this.
I tried getting all the properties using Objc runtime and did something like this:
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 stringWithUTF8String:property_getName(property)];
const char * propAttr = property_getAttributes(property);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#"\""];
if (attrArray.count > 0) {
NSString *propType = [attrArray objectAtIndex:1];
if ([propType containsString:#"NSString"]) {
[self setValue:#"" forKey:name];
}
}
}
free(propertyArray);
This is working like a charm for me. The only issue is I have inherited classes and this code only sets the values for child class, it doesn't sets values of properties in base class. I'm using xcode 6.3.1 & iOS 8.x. Any help is much appreciated. Thanks
You may define a recursive method setDefaultPropValuesForClass: in your bean/data base class, e.g. Bean, and invoke it from base class init method. See the implementation below:
#interface Bean : NSObject
// Add your props
// ...
// .....
#end
#implementation Bean
- (instancetype)init {
self = [super init];
if (self) {
[self setDefaultPropValues];
// TODO: All other initializations
}
return self;
}
- (void)setDefaultPropValues {
[self setDefaultPropValuesForClass:self.class];
}
- (void)setDefaultPropValuesForClass:(Class)refClass {
if (![refClass isSubclassOfClass:[Bean class]]) {
return;
}
// First set default property values in super classes
Class baseClass = class_getSuperclass(refClass);
[self setDefaultPropValuesForClass:baseClass];
//
unsigned int numberOfProperties = 0;
objc_property_t *propertyArray = class_copyPropertyList(refClass, &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++)
{
objc_property_t property = propertyArray[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
const char * propAttr = property_getAttributes(property);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *allAttrs = [propString componentsSeparatedByString:#","];
// Check if property is readonly
if (NSNotFound == [allAttrs indexOfObject:#"R"]) {
// Find Property type token
NSArray * attrArray = [propString componentsSeparatedByString:#"\""];
if (attrArray.count > 1) {
Class propType = NSClassFromString([attrArray objectAtIndex:1]);
if ([propType isSubclassOfClass:[NSString class]]) {
[self setValue:#"" forKey:name];
}
}
}
}
free(propertyArray);
}
#end
Can you check if your class is a subclass by
[self class] is SubclassOfClass: then get copy of property list of base or super class.
objc_property_t *propertyArray = class_copyPropertyList([[self class]superclass], &numberOfProperties);
Related
I have a model class object which has the following properties
#property (nonatomic, strong) NSString *place;
#property (nonatomic, strong) NSString *date;
I am setting these properties from a controller class object.
I want to perform a null check of my model class object properties. So I want to write a for loop like following.
for (property in modelObject)
{
if (object == [NSNull null])//object is a property of modelobject
{
//the next two lines wont be true but need to check that
if([property isKindOfClass:[NSString Class]]) property = #"no place";
if([property isKindOfClass:[NSDate Class]]) property = #"No date;
}
}
My question is,
If the model class object property is set to null how can I check if the property is null and also the declaration type of that property?
Directly checking the two properties instead of looping through the properties won't be helpful because in the actual scenario there are lot of properties with different types for the model class.
Thanks in advance.
Add a method in Model Class
- (void)nullCheck {
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];
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property)];
id propertyValue = [self valueForKey:(NSString *)propertyName]; //check propertyValue here
//set property value here
[self setValue:#"some value" forKey:(NSString *)propertyName];
const char * type = property_getAttributes(property);
NSString *attr = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:#","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
}
free(properties);
}
First, you cant do this to check the properties on the modelObject view controller, if I understand correctly that that is what you want to do :
for (property in modelObject)
What this would do is iterate through an array called modelObject. So if you had first added your properties to that array, that would be fine. There is no simple way to iterate through every property that any class has - you just wouldnt want to do that anyway. You first need to either add them to an array for reference, or refer to them in some other collection type.
Second, you can quickly check if a property remains nil - ie it has never been used, like this :
if (object) {
}
This will return true if the object is not 'nil'. You dont have to use [NSNull null] for what you are trying to achieve. So you could instead do this :
if (!place) {
place = #"no place";
}
if (!date) {
date = #"no date";
}
Say I have n variables
NSNumber* A = #(1);
NSNumber* B = #(2);
NSNumber* C = #(3);
NSNumber* D = #(4);
NSNumber* E = #(5);
...
I need a dictionary like
{#"A":#(1), #"B":#(2), #"C":#(3), #"D":#(4), ... }
One can imagine a more cumbersome example that would be tedious to type out
I think saw a C style function for it but I can't remember. Something like NSDictionaryForVariables()
The C preprocessor macro (not a function) you're looking for is NSDictionaryOfVariableBindings. However, outside of Auto Layout (and, debatably, even there), it's not really a great idea to be setting up dependencies between runtime and compile-time identifiers like that.
Depending on what you're trying to actually accomplish, Key-Value Coding might be a better solution.
It's not a good approach you may find another way to solve your issue but if you want to have an idea about your requested solution here here my try
Our properties
#interface TestyViewController ()
#property (nonatomic) NSNumber* a;
#property (nonatomic) NSNumber* b;
#property (nonatomic) NSNumber* c;
#property (nonatomic) NSNumber* d;
#end
Set the values
- (void)viewDidLoad {
[super viewDidLoad];
self.a=#(1);
self.b=#(2);
self.c=#(3);
self.d=#(4);
}
Get our instance variables
-(NSArray *)propertyNames{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Create the dictionary
- (IBAction)buttonClicked:(id)sender
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString* varName in [self propertyNames])
{
[dict setObject:[self valueForKey:varName] forKey:varName];
}
NSLog(#"%#",dict);
}
result
2015-07-15 20:30:56.546 TestC[879:27973] {
a = 1;
b = 2;
c = 3;
d = 4;
}
I want to transform an instance of an object into an instance of a subclass of that object class, so that I can use the additional methods and properties of that subclass, in Objective-C.
How can I do this in a way that does not require me to hardcode the properties of that object class in a copy method?
It is not possible to transform an object into an instance of a subclass in Objective-C. However, with the class below you can supply an instance of both the object and the subclass and have the values of all properties copied to the subclass instance. This implementation works with both Objective-C object types and C primitives. You do not have to specify (or indeed even determine) the properties that need to be copied, providing you know that the important variables are visible and can be set (i.e., there are no properties that are exposed as "read only" or not exposed at all, whose values cannot be recalculated by the class). This method is thus relatively robust for known classes and will not require updating to support future changes you make in your object class that fit these parameters. It is iOS 8 compatible.
This class provides four class methods:
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
Copies all properties of object to subclassObject. If the subclassObject is not a subclass of object, nil is returned.
+ (NSDictionary *) propertiesOfObject:(id)object;
Returns a dictionary of all visible properties of an object, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfClass:(Class)class;
Returns a dictionary of all visible properties of a class, including those from all its superclasses (other than NSObject).
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
Returns a dictionary of all visible properties that are specific to a subclass. Properties for its superclasses are not included.
Header:
// SYNUtilities.h
#import <Foundation/Foundation.h>
#interface SYNUtilities : NSObject
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject;
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
#end
Implementation:
#import "SYNUtilities.h"
#import <objc/runtime.h>
#import <objc/message.h>
#implementation SYNUtilities
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
{
if (![[subclassObject class] isSubclassOfClass:[object class]]) {
return nil;
}
NSDictionary * properties = [self propertiesOfObject:object];
NSLog(#"Properties of %#:\n%#", [object class], properties);
for (NSString * property in properties) {
SEL selector = NSSelectorFromString(property);
if (selector) {
id value = [object valueForKey:property];
[subclassObject setValue:value forKey:property];
}
}
return subclassObject;
}
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
if (class == NULL) {
return nil;
}
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
I needed to create a subclass of NSTextFieldCell, used in an NSTableView, and wanted to keep the properties intact that were set for the cell in Interface Builder.
I solved the task by using NSKeyedArchiver, which is made to store and restore an object's properties.
Since NSTextFieldCell implements initWithCoder, it supports the archiver functions, and therefore I could use this code to init my subclass from the other's properties:
- (id)initWithCell:(NSCell *)cell {
// Use NSArchiver to copy the NSCell's properties into our subclass
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[cell encodeWithCoder:arch];
[arch finishEncoding];
NSKeyedUnarchiver *ua = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
self = [self initWithCoder:ua];
// Here I'd set up additional properties of my own class
return self;
}
I've been trying to get the list of the properties from the NSObject protocol, but I've been having some trouble.
Using the following code to seems to constantly return nil fro the list of properties on that protocol.
Protocol *protocol = objc_getProtocol("NSObject");
NSLog(#"%s", protocol_getName(protocol)); // confirms that NSObject protocol is found
unsigned count;
objc_property_t *properties = protocol_copyPropertyList(protocol, &count);
for (NSUInteger index = 0; index < count; index++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[index])];
[propertiesArray addObject:key];
}
free(properties);
NSLog(#"%#",propertiesArray); // prints empty array
By all accounts this should work. I have even created my own protocol and used it in place of NSObject and it works fine.
#protocol TestProtocol
#property NSString *someSortOfProperty;
#end
The same code as above with only the name of the protocol changed returns the someSortOfProperty property.
Protocol *protocol = objc_getProtocol("TestProtocol");
NSLog(#"%s", protocol_getName(protocol)); // confirms that TestProtocol protocol is found
unsigned count;
objc_property_t *properties = protocol_copyPropertyList(protocol, &count);
for (NSUInteger index = 0; index < count; index++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[index])];
[propertiesArray addObject:key];
}
free(properties);
NSLog(#"%#",propertiesArray); // prints [85711:303] ( someSortOfProperty )
If any of you have succeeded in doing this in the past, or can spot something that I've done wrong that would be great.
I have this function that is storing the contents of an array in three different variables. My question is how can I access to the contents stored in _company.address from another function in the same class.
-(NSArray *) csvArray2CompaniesArray:(NSArray *) csvArray
{
int i=0;
NSMutableArray *ma = [[NSMutableArray alloc] init];
for (NSArray * row in csvArray)
{
if (i>0)
{
Company *_company = [[Company alloc] init];
_company.name = [row objectAtIndex:0];
_company.address = [row objectAtIndex:1];
_company.telephone = [row objectAtIndex:2];
[ma addObject:_company];
}
i++;
}
return (NSArray *) ma;
}
Thank you in advance.
You cannot the accessibility of an object is private in a function.Either you declare it as global or declare it in class's scope.Better in .h file
You can use the functios value as :-
YourViewControllerWithFunction *accessFunc=[[YourViewControllerWithFunction alloc]]init];
Company *_company=[accessFunc csvArray2CompaniesArray:youInputArray];
[_company objectAtIndex:intVallue];//Use in loop
You should correct the return value of your function to NSMutableArray or make a copy to get the NSArray of the NSMutableArray you created.
After that, you can access the contents of your array from anywhere inside your class like:
for (Company *c in [self csvArray2CompaniesArray:csvarray])
{
NSLog(#"%#", c.address);
}