Objective c - create a variable name at runtime and evaluate its value - ios

I have a few keys defined as static vars:
static NSString icon_0 = #"image_0.png";
static NSString icon_1 = #"some_image_with_a_different_name.png";
static NSString icon_3 = #"picure_of_a_bear.png";
now inside a datasource method where I get the indexpath i would like to create the variable name from a string:
-(UICollectionviewCell*)cellForIndexPath:(NSIndexPath *)path
{
NSString *varName = [NSString stringWithFormat:#"icon_%d",path.row];
// here I need the static NSString which corresponds to the var name created
// i.e
NSString imageName;
if (indexPath.row == 0)
{
imageName = #"image_0.png";
}
// would be much nicer to do something like
NSString *imageName = [varName evaluate]; // get the content out of it...
}
How can I do this on static variable?
I tried
NSString *iconName = [self valueForKey:str];
but it isn't an iVar so not working...

i'd not use static vars but a static dictionary like this:
runnable example:
#import <Foundation/Foundation.h>
NSDictionary *DDImageName(NSString *varName);
NSDictionary *DDImageName(NSString *varName) {
static NSDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO
//add all names and the image names here
dict = #{#"icon_0": #"image_0.png",
#"icon_1": #"some_image_with_a_different_name.png",
#"icon_2": #"picure_of_a_bear.png"};
});
return dict[varName];
}
//demo only
int main(int argc, char *argv[]) {
#autoreleasepool {
NSString *varName = #"icon_0";
NSString *imgName = DDImageName(varName);
NSLog(#"imageName for %# = %#", varName, imgName);
}
}

If you make your variables instance variables or properties of an object, then you could use key value coding (KVC) to read and write values to them:
-(UICollectionviewCell*)cellForIndexPath:(NSIndexPath *)path
{
NSString *varName = [NSString stringWithFormat:#"icon_%d",path.row];
// here I need the static NSString which corresponds to the var name created
// i.e
NSString imageName;
if (indexPath.row == 0)
{
[self setValue = #"image_0.png" forKey: varName];
}
}
or
string = [self valueForKey: varName];
As #Daij-Djan points out, though, it's probably better to refactor your code to save your information to a dictionary rather than trying to manipulate your instance variables using string variable names. KVC is fairly slow, and will crash your program if a key doesn't exist at runtime, so it's fragile.

Related

VIDEO_TOO_LONG_TITLE alert shown by UIImagePickerController

I use a UIImagePickerController to take movies. The length of a movie is limited by setting the videoMaximumDuration property of the controller.
When one tries to take a longer movie, an alert is shown, as expected.
However an unexpected additional alert entitled VIDEO_TOO_LONG_TITLE is shown directly above the controls (see image below).
Apparently this is an iOS bug (only partly localized, no clip has been selected).
Is it possible to hide this unnecessary and inappropriate alert?
imagePicker.allowsEditing = false
I know the question is quite old, but here's a solution to the problem that is still not addressed by Apple.
#implementation NSBundle (UIImagePickerControllerLocalizationFix)
+ (void) load {
SEL const selector = #selector (localizedStringForKey:value:table:);
Method const localizedStringMethod = class_getInstanceMethod (self, selector);
NSString *(*originalImp) (NSBundle *, SEL, NSString *, NSString *, NSString *) = (typeof (originalImp)) method_getImplementation (localizedStringMethod);
IMP const updatedImp = (typeof (updatedImp)) imp_implementationWithBlock (^(NSBundle *bundle, NSString *key, NSString *value, NSString *tableName) {
NSString *const result = originalImp (bundle, selector, key, value, tableName);
if ([key isEqualToString:#"VIDEO_TOO_LONG_TITLE"] && [result isEqualToString:key]) {
static NSBundle *properLocalizationBundle = nil;
static NSString *properLocalizationTable = nil;
static dispatch_once_t onceToken;
dispatch_once (&onceToken, ^{
NSString *const originalBundleName = bundle.infoDictionary [(NSString *) kCFBundleNameKey];
NSArray <NSBundle *> *const frameworkBundles = [NSBundle allFrameworks];
for (NSBundle *frameworkBundle in frameworkBundles) {
NSString *const possibleTableName = [originalBundleName isEqualToString:tableName] ? frameworkBundle.infoDictionary [(NSString *) kCFBundleNameKey] : tableName;
NSString *const localizedKey = originalImp (frameworkBundle, selector, key, value, possibleTableName);
if (![localizedKey isEqualToString:key]) {
properLocalizationBundle = frameworkBundle;
properLocalizationTable = possibleTableName;
break;
}
}
if (!(properLocalizationBundle && properLocalizationTable)) { // Giving up
properLocalizationBundle = bundle;
properLocalizationTable = tableName;
}
});
return originalImp (properLocalizationBundle, selector, key, value, properLocalizationTable);
} else {
return result;
}
});
method_setImplementation (localizedStringMethod, updatedImp);
}
#end

Should I use function parameters in block, or keep them as __block variables

When I use a instance variable inside a block, the best practice is to add __block before it, so the scope is async, and I will be able to use it after.
Example:
-(void) someFunction {
__block NSString *nameIWillUseInBlock = #"Some name"; //Here ill use the __block
[self functionWithCallback:^(NSString *string) {
NSString *stringWithString = [NSString stringWithFormat:#"%#%#", nameIWillUseInBlock, #"with bla"];
//Here Ill do somthing with this string
}];
}
So my question is, what about params passed to the function, can I use them like so:
-(void) someFunctionWithParam:(NSString *) nameIWillUseInBlock {
[self functionWithCallback:^(NSString *string) {
NSString *stringWithString = [NSString stringWithFormat:#"%#%#", nameIWillUseInBlock, #"with bla"];
//Here Ill do somthing with this string
}];
}
Or should I keep the param as a __block instance like so:
-(void) someFunctionWithParam:(NSString *) nameIWillUseInBlock {
__block NSString *asyncNameIWillUseInBlock = nameIWillUseInBlock;
[self functionWithCallback:^(NSString *string) {
NSString *stringWithString = [NSString stringWithFormat:#"%#%#", asyncNameIWillUseInBlock, #"with bla"];
//Here Ill do somthing with this string
}];
}
And if I should use the params, and not as a __block instance, how does the scope know witch params did pass before the current scope?
Thanks.
Here is a really good apple doc on the topic Working With Blocks
I think the section "Blocks Can Capture Values from the Enclosing Scope" is what you are looking for specifically.
"In this example, anInteger is declared outside of the block, but the value is captured when the block is defined.
Only the value is captured, unless you specify otherwise. This means that if you change the external value of the variable between the time you define the block and the time it’s invoked"
So if you look at this:
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(#"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock();
The log will show
Integer is: 42
"Use __block Variables to Share Storage"
__block int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(#"Integer is: %i", anInteger);
};
anInteger = 84;
testBlock();
The log will show
Integer is: 84
You can youse parameter directly in block you can use this code.
-(void) someFunctionWithParam:(NSString *) nameIWillUseInBlock {
[self functionWithCallback:^(NSString *string) {
NSString *stringWithString = [NSString stringWithFormat:#"%#%#", nameIWillUseInBlock, #"with bla"];
//Here Ill do somthing with this string
}];
}

Set Default values of NSString properties automatically

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);

Transform (or copy) an object to a subclass instance in Objective-C

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;
}

How to use Static Variable to save a string in objective-c?

I want to use Static Var to save a NSString.
So I define a Static Var in a .h file like this:
#ifndef GlobalParameters_h
#define GlobalParameters_h
//access token
static NSString *applicationToken;
#endif
In class A, I change the static var like this:
#import "ClassA.h"
#import "GlobalParameters.h"
extern NSString *applicationToken;
#implementation ClassA
+ (void)parseResponse:(NSString *)response
{
NSDictionary *responseDic = [response objectFromJSONString];
NSString *token = [responseDic objectForKey:#"token"];
applicationToken = [token copy];
NSLog(#"%#",applicationToken);
}
When the debugger run to
applicationToken = [token copy];
I found the "applicationToken" is nil,but the next sentence
NSLog(#"%#",applicationToken);
can output the right value in console! And in ClassB , the "applicationToken" is nil too.
I don't know why the static var is nil. I think the compiler will find the definition of "applicationToken" in GlobalParameters.h.But why I can't modify the static value?
Thanks for your help:)
static global variable mean that it's own for every object-file it's used. So there is willbe own applicationToken for ClassA, ClassB.
To create global variable for all object-files you need this:
In GlobalParameters.h:
#ifndef GlobalParameters_h
#define GlobalParameters_h
//access token
extern NSString *applicationToken;
#endif
In GlobalRarameters.m:
#import "GlobalParameters.h"
NSString *applicationToken;
P.S. I hope you use ARC, because if not, then applicationToken = [token copy]; will cause memory leaks.
Hey Its working fine , I am checking like this
Once check the are you getting "responseDic" (or) not , Check the
The dictionary have token key
static NSString *applicationToken;
applicationToken = #"srinivas";
NSLog(#"%#",applicationToken);
NSDictionary *responseDic = [NSDictionary dictionaryWithObject:#"static" forKey:#"token"];
NSString *token = [responseDic objectForKey:#"token"];
applicationToken = [token copy];
NSLog(#"%#",applicationToken);
[AppDelegate parseResponse:responseDic];

Resources