Modify object type at runtime - objective-c

Is it possible in Objective C to modify an object type at runtime without the compiler complaining?
e.g.
id object;
in an init method
initWithType:(someEnumType) type
then depending on type set the object to a class type.
How is this done without the compiler flagging errors that object does not declare someMethod?

The most common way to do something like that is in a factory method, rather than an initializer:
typedef enum {
etString,
etNumber
} EnumType;
#implementation MyFactory
+(id)makeNewObjectWithType:(EnumType)et {
id res;
switch (et) {
case etString:
res = [NSString string];
break;
case etNumber:
res = [NSNumber numberWithInt:12345];
break;
default:
res = nil;
break;
}
return res;
}
#end

Related

check if object has been declared in objective c

is it possible to check if an object is declared?
e.g.
if(object) {
//object is known
} else
{
int object;
}
if I do it like this I'm getting the error "use of undeclared identifier 'object'"
Just hold an empty pointer, and initialize it when you need it.
id object = nil;
.... some stuff here
if (!object) {
object = [SomeClass new];
}
.... your job with that object

Objective-C:How to Converting int to id for selector's object?

I received a return value from SQLite fetch
int primaryKey = sqlite3_column_int(statement, 0);
and I'm going to use it as a selector's object:
[[ABC alloc] performSelector:#selector(abcWithAAA:) withObject:[NSNumber numberWithInt:primaryKey]];
the NSLog result for primarykey is a number 4:
NSLog(#"primaryKey:%i",primaryKey);
4
but the NSLog result for [NSNumber numberWithInt:primaryKey] is 131628896.
why? and how do i convert the int value correctly?
Thanks!
I solved the problem using an adapter method that does the cast for the withObject method.
My problem was that I wanted to use a typedef enum and pass it as value to the withObject.
I wanted to call this method using the performSelect message:
-(void) requestInfosAndPersistByMonsterType:(MonsterTypes)monsterType {
}
As you see it request a MonsterTypes typedef defined like this:
typedef enum
{
MonsterTypeIWerwolf = 0,
MonsterTypeITempler = 1,
MonsterTypeIUndefined,
} MonsterTypes;
Actually to be able to call the method above I build this adapter that calls it then:
-(void)monsterTypeFromObject:(id)_monsterType {
if ([_monsterType respondsToSelector:#selector(intValue)]) {
int _t = [_monsterType intValue];
switch (_t) {
case MonsterTypeIWerwolf:
_t = MonsterTypeIWerwolf;
break;
case MonsterTypeITempler:
_t = MonsterTypeITempler;
break;
default:
_t = MonsterTypeIUndefined;
break;
}
[self requestInfosAndPersistByMonsterType:_t];
}
}
It is used this way:
[self performSelector:#selector(monsterTypeFromObject:) withObject:[NSNumber numberWithUnsignedInt:monsterType] afterDelay:5.0f];
You can find it explained in more detail here:
http://kerkermeister.net/objective-c-adapter-from-nsinteger-to-id-when-using-performselector-withobject/
When you log [NSNumber numberWithInt:primaryKey], you're logging the address of an NSNumber object. If you want to see what's inside it should be [[NSNumber numberWithInt:primaryKey] intValue].
In other words, there's nothing in that to suggest your conversion is a problem.
[NSNumber numberWithInt:primaryKey] is object.
use %# for objects.
NSLog(#"%#", [NSNumber numberWithInt:primaryKey]);
131628896 is the memory address of the NSNumber object.
use:
- (void)abcWithAAA: (NSNumber *)number {
int primaryKey = [number intValue];
NSLog("%i", primaryKey);
}

Objective C: How to Write a Instantiating Custom Init

Very basic question, but I have an error in my code that can only be answered by one assumption: my class isn't being instantiated!
I haven't written much in Objective C in some time, and I was never really good, so please point out even the most painfully obvious.
I am using:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
ObjectSelectionViewController *volume = [[ObjectSelectionViewController alloc] initWithMeasureType:2];
NSLog(#"%#", [length measurementType]);
NSLog(#"%#", [mass measurementType]);
NSLog(#"%#", [volume measurementType]);
The NSLogs return whichever measurement was assigned last, regardless of the separate allocs and inits.
Here is the constructor of the ObjectSelectionViewController class:
#import "ObjectSelectionViewController.h"
#implementation ObjectSelectionViewController
NSString *measurementType;
-(ObjectSelectionViewController*) initWithMeasureType:(int)value
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
-(NSString*) measurementType
{
return measureType;
}
Thanks for the help, it's driving me crazy!
You need to make measureType an instance variable, so that each object of this type that you create has its own copy:
#interface ObjectSelectionViewController : NSViewController {
NSString * measureType; // Declare an NSString instance variable
}
- (id) initWithMeasureType: (int)value;
#end
As it is, there is only one copy of the variable, and every time you instantiate a new object, its value changes. Since each instance is referring to the same copy, they all get the same value:
ObjectSelectionViewController *length = [[ObjectSelectionViewController alloc] initWithMeasureType:0];
NSLog(#"%#", [length measurementType]); // Prints "Length"
ObjectSelectionViewController *mass = [[ObjectSelectionViewController alloc] initWithMeasureType:1];
NSLog(#"%#", [length measurementType]); // Prints "Mass"
You also need to change your init... method as mentioned by other answerers:
- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}
Since you are assigning a literal string to the instance variable, you do not need to worry about managing its memory; if you were doing anything more complicated, you would probably do well by declaring a property.
Another note: initializer methods should always return id, a generic object pointer, to allow subclasses to work properly.
You need to call [super init] first, like this:
-(id) initWithMeasureType:(int)value
{
if ((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}
Constructors are a convention in Objective-C rather than a language feature. So, for example, there's no automatic calling of parent constructors (just like you wouldn't expect any other overridden method to call its parent implementations). Similarly, the names used for constructors are just conventions, so the compiler knows nothing of init. With that in mind, what you actually want as your constructor is:
-(id) initWithMeasureType:(int)value
{
if((self = [super init]))
{
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
}
return self;
}
Assuming measureType is an instance variable (declared as part of the interface, generally speaking) and not a global then that should do what you want.
In your custom init method you just need to start with:
self = [super init];
- (id) initWithMeasureType: (int)value {
// Call superclass's initializer
self = [super init];
if( !self ) return nil;
switch (value) {
case 0: // Length
measureType = #"Length";
break;
case 1: // Mass
measureType = #"Mass";
break;
case 2: // Volume
measureType = #"Volume";
break;
}
return self;
}

the best way to implement readable switch case with strings in objective-c?

In other dynamic languages like ruby, javascript etc. you can do simply this:
switch(someString) {
case "foo":
//do something;
break;
case "bar":
// do something else;
break;
default:
// do something by default;
}
In objective-c, because it's derived very colsely from c language, you can't do that. My best practice for this is:
#import "CaseDemo.h"
#define foo 1
#define bar 2
static NSMutableDictionary * cases;
#implementation CaseDemo
- (id)init
{
self = [super init];
if (self != nil) {
if (cases == nil) {
// this dict can be defined as a class variable
cases = [[NSMutableDictionary alloc] initWithCapacity:2];
[cases setObject:[NSNumber numberWithInt:foo] forKey:#"foo"];
[cases setObject:[NSNumber numberWithInt:bar] forKey:#"bar"];
}
}
return self;
}
- (void) switchFooBar:(NSString *) param {
switch([[cases objectForKey:param] intValue]) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
#end
It's seems to be ok, but #define makes foo and bar like a reserved word, and I can't use in my code. If I replace define constants with class constants, this problem is fixed, because in other classes I must use MyClassName before the constant name. But how can I minimize the object allocation for this simple task? Someone have a "better practice" for this?
EDIT:
The code below is what I wanted to do, but it's a little bit unconfortable to get the values of the enum or #define. Because I created an application what have just an input where I can write the string to get that hash and go back to xcode and set the values for the enums. So my problem is I can't do that in runtime time, because of the main behavour of switch case statement... Or if I do that with that NSDictionary way -> its have a lot of overhead compared with this solution.
#import "CaseDemo.h"
typedef enum {
foo = 1033772579,
bar = -907719821
} FooBar;
unsigned int APHash(NSString* s)
{
const char* str = [s UTF8String];
unsigned int len = [s length];
unsigned int hash = 0xAAAAAAAA;
unsigned int i = 0;
for(i = 0; i < len; str++, i++)
{
hash ^= ((i & 1) == 0) ? ( (hash << 7) ^ (*str) * (hash >> 3)) :
(~((hash << 11) + ((*str) ^ (hash >> 5))));
}
return hash;
}
#implementation CaseDemo
- (void) switchFooBar:(NSString *) param {
switch(APHash(param)) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
#end
NOTE: the hash function can defined elsewhere in common namespace to use it anywhere, typically I create a Utils.h or Common.h for this kind of stuff.
NOTE2: In "real word" we need to use some cryptographic hashing function, but now I used the algorithm by Arash Partow to keep the example simple.
So, my final question: Is there a way to evaluate these values with the preprocessor somehow? I think no, but maybe? :-)
Something like:
// !!!!!! I know this code is not working, I don't want comments about "this is wrong" !!!!
// I want a solution to invoke method with preprocessor, or something like that.
typedef enum {
foo = APHash(#"foo"),
bar = APHash(#"bar")
} FooBar;
UPDATE: I found a "maybe solution" but it seems to be work with g++ 4.6> only. generalized constant expressions may be do it for me. But I'm still testing...
typedef enum {
foo,
bar
} FooBar;
- (void) switchFooBar:(NSString *) param {
switch([[cases objectForKey:param] intValue]) {
case foo:
NSLog(#"its foo");
break;
case bar:
NSLog(#"its bar");
break;
default:
NSLog(#"its default");
break;
}
}
NSString * extension = [fileName pathExtension];
NSString * directory = nil;
NSUInteger index = [#[#"txt",#"png",#"caf",#"mp4"] indexOfObjectPassingTest:^
BOOL(id obj, NSUInteger idx, BOOL *stop)
{
return [obj isEqualToString:extension];
}];
switch (index)
{
case 0:
directory = #"texts/";
break;
case 1:
directory = #"images/";
break;
case 2:
directory = #"sounds/";
break;
case 3:
directory = #"videos/";
break;
default:
#throw [NSException exceptionWithName:#"unkonwnFileFormat"
reason:[NSString stringWithFormat:#"zip file contain nknown file format: %#",fileName]
userInfo:nil];
break;
}
The technique is extracted from production code and modified to use colors for this example. The premise is that a string comes in with the text name of a color from some external feed. This inbound color name is matched against known Crayola color names in the system. If the new color name matches any known Crayola color name strings, the numeric value for HTML hex code equivalent of that Crayola color name is returned.
First use http://www.unit-conversion.info/texttools/crc/ and put all of your known Crayola color names through it to get numerical equivalents. These will be used in the case statements. Then put those values into an enumerated for cleanliness (e.g. LivingColors below). These numbers become equivalent to the actual color name string.
Then at run time the variable text is put through the same function, but internal to your code, to generate the same kind of numeric constant. If the numeric constant from the code matches the statically generated constant, then the text strings that they represent are exactly equal.
The internal code function is crc32() found in zlib.h. This generates a unique number based upon the text put through it just like the web page converter above. The unique number from crc32() can then be used in a common C switch() statement to match against the known colors which were pre-processed into numbers into the enumerated.
To use the native system function crc32() to generate CRC32B values, include the /usr/lib/libz.1.dylib in your project for linking. Be sure to include or #import <zlib.h> in your source that references crc32()
Implement an Objective C category on NSString to make the native NSString class understand the crc32: and htmlColor: messages.
Finally, read/get the name of the color into an NSString object, then send the string the htmlColor: message, it switches to match the 'strings' and returns the HTML hex equivalent value for a Crayola color name.
#import <zlib.h>
#define typedefEnum( enumName ) typedef enum enumName enumName; enum enumName
/**
#see Crayola Web Colors https://www.w3schools.com/colors/colors_crayola.asp
#see CRC32B value generator for static case strings http://www.unit-conversion.info/texttools/crc/ or http://www.md5calc.com
*/
#define typedefEnum( enumName ) typedef enum enumName enumName; enum enumName
typedefEnum( LivingColors ) {
kRedColor = 0xc22c196f, // "Red" is 0xED0A3F in HTML
kBlueberryColor = 0xfbefa670, // "Blueberry" is 0x4F86F7 in HTML
kLightChromeGreenColor = 0x44b77242, // "Light Chrome Green" is 0xBEE64B in HTML
kPermanentGeraniumLakeColor = 0xecc4f3e4, // "Permanent Geranium Lake" is 0xE12C2C in HTML
kIlluminatingEmeraldColor = 0x4828d5f2, // "Illuminating Emerald" is 0x319177 in HTML
kWildWatermelonColor = 0x1a17c629, // "Wild Watermelon" is 0xFD5B78 in HTML
kWashTheDogColor = 0xea9fcbe6, // "Wash the Dog" is 0xFED85D in HTML
kNilColor = 0xDEADBEEF // could use '0' but what fun is that?
};
// generates the CRC32B, same used to check each ethernet packet on the network you receive so it’s fast
- (NSUInteger) crc32 {
NSUInteger theResult;
theResult = (NSUInteger)crc32( 0L,
(const unsigned char *) [self UTF8String],
(short)self.length);
return theResult;
}
/// #return the HTML hex value for a recognized color name string.
- (NSUInteger) htmlColor {
NSUInteger theResult = 0x0;
LivingColors theColorInLivingColor = kNilColor;
theColorInLivingColor = (LivingColors) [self crc32];
// return the HTML value for a known color by effectively switching on a string.
switch ( theColorInLivingColor ) {
case kRedColor : {
theResult = 0xED0A3F;
}
break;
case kBlueberryColor : {
theResult = 0x4F86F7;
}
break;
case kLightChromeGreenColor : {
theResult = 0xBEE64B;
}
break;
case kPermanentGeraniumLakeColor : {
theResult = 0xE12C2C;
}
break;
case kIlluminatingEmeraldColor : {
theResult = 0x319177;
}
break;
case kWildWatermelonColor : {
theResult = 0xFD5B78;
}
break;
case kWashTheDogColor : {
theResult = 0xFED85D;
}
break;
case kNilColor :
default : {
theResult = 0x0;
}
break;
}
return theResult;
}
For the example an Objective C Category was made to add the two methods the existing Cocoa class NSString, rather than subclass it.
The end result is that an NSString object appears to have the ability to natively get a CRC32B value of itself (very handy beyond this example) and can essentially switch() on the color’s name string that possibly came in from the user, a text file, etc. to identify a match much faster than any text string comparison can occur.
Fast, efficient, and reliable, this approach can easily be adapted to any kind of variable text matching to static known value text. Bear in mind that CRC32B checksums are generated by bitwise operations and the C switch statement use bitwise operations optimized at compile time. Remember this is speedy because CRC32B is the highly optimized function used to check each ethernet packet your Mac/iPhone/iPad receives... even when you download multi-gigabyte files like macOS Sierra.

Multiple methods warning

I'm currently learning Objective C and in the process I've made the silly little program below. The program compiles fine - however I get the warning "multiple methods named '-setName:' found".
I've only interfaced and implemented the method once.
What does this warning mean, and how do I correct it?
#import <Foundation/Foundation.h>
// these are the three yoga-exercises we can perform
typedef enum {
kCobra,
kUniversal,
kDog
} ExerciseName;
// translating our variables into human
NSString *nameExercise (ExerciseName nameExercise)
{
switch (nameExercise) {
case kCobra:
return #"Cobra Pose";
break;
case kUniversal:
return #"Universal Stretch";
break;
case kDog:
return #"Dog Pose";
break;
}
return #"no clue!";
} // nameExercise
#interface Exercise : NSObject
{
ExerciseName name;
}
-(void) setName: (ExerciseName) name;
-(void) exerciseDo;
#end
#implementation Exercise
-(void) setName: (ExerciseName) n {
name = n;
} // setName
-(void) exerciseDo {
NSLog(#"Exercise: %#",
nameExercise(name));
}
#end
void executeExercises(id exercises[], int count) {
int i;
for(i=0; i<count; i++) {
id exercise = exercises[i];
[exercise exerciseDo];
}
}
int main (int argc, const char * argv[]) {
id exercises[1];
exercises[0] = [Exercise new]; // initiating an object of class Exercise
[exercises[0] setName:kDog];
executeExercises(exercises, 1);
return 0;
} //main
the meaning of the message is that there are multiple selectors with the name setName: in the translation (that is, it is declared in at least on other place among all included headers). the compiler may choose the wrong selector (which can introduce undefined behavior).
you can typically correct the problem using one (or more) of the following approaches:
1) rename the method to a unique name: e.g. setExerciseName may be ok, if not used in other translations.
2) match the signature of the other selector. e.g. setName:(NSString *)name
3) use type safety:
Exercise * ex = [Exercise new];
[ex setName:kCobra];
4) cast the variable to the type: [(Exercise*)exercise setName:kCobra];
5) restore the type with a new variable: Exercise * ex = exercise;
since you have declared the var as an id, you have erased the type, and it means that the object may respond any visible selector. in general, you should not erase the type in this manner, except when truly necessary.
the best approach i see is a combination of 1 and 3:
[ex setExerciseName:kCobra];