Multiple class instances - objective-c

There's the instance1 and instance2 of the same class (RandomClass).
I want to set the instance1.string as #"instance 1 string" and instance2.string as #"instance2 2 string", and access them differently, but looks like when I set the instance2.string the instance1.string also changes it value. I'm using sharedInstance method;
Something like:
RandomClass *instance1 = [RandomClass sharedInstance];
instance1.string = #"instance 1 string";
RandomClass *instance2 = [RandomClass sharedInstance];
instance2.string = #"instance 2 string";
NSLog(#"%#", instance2.string); // wich results 'instance 2 string'
NSLog(#"%#", instance1.string); // wich results 'instance 2 string'
As you can see, the instance2.string changes the instance1 string value.
Is there any way to create different instances, separately, dealing with the class objects as unique values (accessed by it own instance)?

Your example is not valid because you doesn't have two instances of the same class, you have only one singleton instance. Note that [RandomClass sharedInstance] return always the same instance, you only have two references for it.
The normal way to create instances is
[[RandomClass alloc] init...]
Please don't use the term thread in the context of class and instances, this only makes the problem more confusing.
I believe you should visit some Obj-C tutorials or some OOP tutorials first.

instance1 and instance2 are referencing the same object as [RandomClass sharedInstance]. If you modify any of them, all of the changes will be take affect on the original sharedInstance.
If you want two different objects, you must instantiate two times (if RandomClass provides any initializer method):
RandomClass *instance1 = [[RandomClass alloc] init];
instance1.string = #"instance 1 string";
RandomClass *instance2 = [[RandomClass alloc] init];
instance2.string = #"instance 2 string";
NSLog(#"%#", instance1.string); // wich results 'instance 1 string'
NSLog(#"%#", instance2.string); // wich results 'instance 2 string'
(By the way, RandomClass looks like a Singleton, so there might be no way to instantiate multiple objects.)

Related

Newbie. Custom class

CustomClass *variableName = [[CustomClass alloc] init];
variableName.propertyName = #"Some text";
Could anyone explain this code step by step in human language?
Why if I want to send data to a property in CustomClass I am accessing it throught varibaleName.propertyName , but not through CustomClass.propertyName.
I can not understand it.
If I want to send some data to a varibale in CustomClass wouldn't it be logically to show the path to that property = CustomClass.propertyName = #"Some text"; ?
*variableName - what is it for?
I am confused.
There seems to be some confusion on the difference between an instance and a class. It's generally better to try and link complex ideas like this to real-world examples.
A Class could, for example, be Cars. Thus, you have a Car class. It will include information shared by all Cars. For example, instead of having propertyName it could have a "model" name. To access data about any given car you must first create it. That is what you do in the first line: CustomClass *variableName = [[CustomClass alloc] init];
In our example, we would write Car *myCar = [[Car alloc] init]; which creates a new Car object that we call myCar. Then, you can say myCar.model = "Civic". We do not want to make all cars be a "Civic", but specifically the myCar that we created.
Do not be confused between a Class, which describes a general kind of object, and an Instance, which is the object itself.
Hopefully you now understand the last part of your question:
*variableName - what is it for?
This means that you have a reference to an instance of your CustomClass which is called variableName. In our example, this is myCar which you can then manipulate or change.
You access variableName.propertyName instead of CustomClass.propertyName because variableName is an instance of the class, while CustomClass is the class itself, not the object that you use.
For instance, you have 2 CustomClass objects, lets say variable1 and variable2. variable1.propertyName will be different from variable2.propertyName because they are different instances of the class, not the class itself.

Copying some data of an NSManagedObject to another

I have an NSManagedObject subclass (let's call it) Car with 2 properties of type NSNumber: speed and fuel.
If I have an instance of the class Car and I copy some values to a new instance, are these values connected or are just copied like using the method copy?
Here the code with my doubt:
// The object ferrari was previously obtained from fetch request
Car *lotus = [NSEntityDescription insertNewObjectForEntityForName:#"Repetition"
inManagedObjectContext:self.managedObjectContext];
// solution 1
lotus.speed = ferrari.speed;
lotus.fuel = ferrari.fuel;
// solution 2
lotus.speed = [ferrari.speed copy];
lotus.fuel = [ferrari.fuel copy];
Giving that I want to be sure that the two object are independent, so changing properties or lotus doesn't have to change properties of ferrari can I just use the solution 1?
NSNumber objects are immutable. If you assign
lotus.speed = ferrari.speed;
then both attributes point to the same NSNumber object, but you cannot change
that object. You can only assign a new object:
ferrari.speed = #(454);
but that will not change the value of lotus.speed. So you don't need to copy the
object. The same is true for NSString attributes.

Getting the class type for a nil object?

If I have an object that is already allocated, then doing object.class returns a non-nil value. So far so good. But, if the object has not yet been allocated, then accessing object.class returns nil.
I want to allocate an object based on its type dynamically, so for example:
#property NSArray *myArray;
...
// myArray is nil so far
self.myArray = [_myArray.class new];
However, I can't do this because _myArray.class is returning nil. So how would I determine the class type of a nil instance?
Update:
It is in fact possible. Check out my answer below.
You cannot determine the class of a nil instance, because it does not have one: it can be, quite literally, of any type derived from the type of the variable. For example, NSMutableArray is perfectly compatible with NSArray:
NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK
Since the run-time capabilities of different subclasses can vary a lot, it is always up to your program to decide what kind of objects it wants.
Objective-C is a duck-typed language. This means that there are several things you can or can't do, and one of the things you can't is statically get a reference to the type of a variable.
Specifically, in your expression:
[_myArray.class new]
First, _myArray.class is evaluated, and then the result is sent the new message. Since _myArray is nil to begin with, _myArray.class returns nil as well, and the new message will return nil too, because sending any message to nil returns nil (or the closest representation to zero the return type has). This is why it doesn't work.
I suspect you come from a strongly-typed language like C#; what you're doing right now is the equivalent of Foo foo = (Foo)Activator.CreateInstance(foo.GetType()), which is sure to fail because foo.GetType() will either not compile or throw an exception (depending on if it's a class field or a local variable) since it was never assigned a value. In Objective-C, it compiles but it doesn't works. What you would want is Activator.CreateInstance(typeof(Foo)), but notice that Foo is now hardcoded here too, so you might as well just create a new Foo().
You say that the compiler "knows the type" of the object. This is not exactly true. First, NSArray and NSMutableArray are the root classes of the NSArray class cluster. This means that both are abstract, and [NSArray alloc] and [NSMutableArray alloc] return an instance of a subclass (NSCFArray last time I checked, and possibly something else; I recall seeing _NSArrayM). Maybe [NSArray new] works, but it's not giving you a plain NSArray.
Second, type safety is not enforced. Consider this code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
So even though the compiler thinks that bar is an NSArray, it's in fact a NSString. If we plug in your code:
id foo = #"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];
baz is now an NSString as well. Since you ask for the runtime class of bar, the compiler has nothing to do with the operations.
And precisely because of that kind of behavior, you should probably instantiate your object with a class that you know, using [NSArray new] instead of trusting _myArray to be non-nil, and to be what you think it is.
You must init the property , or it will be nil , send a message to a nil object , it will return nil , so ,you must first init the array like _array = [[NSArray alloc] init];
So, for anyone wondering if this is possible, it is:
objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];
strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);
NSLog(#"%#", objectClass);
Output:
NSArray
Done with the help of extobjc.
Nil has no class type
In Objective-C the actual class on an instance variable is only determined at runtime. So, you can't know the class of a nil object.
This is not an issue in your situation since you only need to do:
NSArray *myArray = [NSArray new];
Or
NSArray *myArray = [[NSArray alloc] init];
In Objective-C most decisions are deferred to the runtime
(as much as possible)
Objective-C is a runtime oriented language, which means that when it's
possible it defers decisions about what will actually be executed from
compile & link time to when it's actually executing on the runtime.
This gives you a lot of flexibility in that you can redirect messages
to appropriate objects as you need to or you can even intentionally
swap method implementations, etc.
This requires the use of a runtime
which can introspect objects to see what they do & don't respond to
and dispatch methods appropriately. If we contrast this to a language
like C. In C you start out with a main() method and then from there
it's pretty much a top down design of following your logic and
executing functions as you've written your code. A C struct can't
forward requests to perform a function onto other targets.
Source: Understanding the Objective-C Runtime

Objective-C: a Pointer That Points to Difference Classes at Different Time

I'm not really experienced with Objective-C. Here is a problem I encountered.
When I want to define a pointer for a particular instance of a class, I can
NSString* foo;
But is it possible to define pointers for instances of classes like this?
x* hotdog; //"x" is the type of pointer hotdog is
hotdog = NSString; //now points to NSString
hotdog* foo; //an instance of NSString is created
hotdog = UIView; //now points to UIView
hotdog* foo; //an instance of UIView is created
How to define the class-pointer hotdog? (what should I replace x with?)
what should I replace x with?
You should replace x with the name of the most specific common ancestor of the classes that you are planning to use with this pointer. In your example, that would be NSObject, because both NSString and UIView inherit it, and there are no other common ancestors. In the worst case, the common ancestor is id.
In general, tricks like that should be avoided in most situations, because reusing a pointer for something really different is bad for readability.
If you want a pointer to an object of a type that's not yet known at compile-time (similar to dynamic in C#), use id:
id hotdog;
hotdog = [[NSString alloc] init];
hotdog = [[NSArray alloc] init];
Only do this when you really need it. If you use it everywhere, your code can easily become a mess since you'll lose track of the type of the variable.
At first I misunderstood your question. I'll leave my old answer here just in case future visitors need it.
The type of pointers to classes is Class and to get an object of that type use +[NSObject class].
Class hotdog = [NSString class]; // now points to NSString
NSString *myString = [[hotdog alloc] init]; // create instance of NSString
hotdog = [NSArray class]; // now points to NSArray
NSArray *myArray = [[hotdog alloc] init]; // create instance of NSArray
You can use either NSObject* or id as the pointer type. NSObject* will accept any subclass of NSObject, while id will accept other Objective-C objects as well.
Note that, to avoid compiler warning messages, you must cast the pointer type back to the (presumably known) actual type before applying any sort of dereferencing operation (other than methods of NSObject).
You can, to be sure you have the expected type of object, use isKindOfClass to check the type:
if ([genericPointer isKindOfClass:[NSArray class]]) {
NSString* arrayElement = [(NSArray)genericPointer objectAtIndex:x];
}
But is it possible to define pointers for instances of classes like this?
I suppose you're asking for the equivalent of C++ templates.
You can't do it and you don't need it, just use the id type:
id foo= #"some text";
If you are working on an instance class the pointer to the class itself is simply self.
If you are working on a class pointer you could just use the id type since it is a generic type. Make sure then the object you are working on is of the expected type by using the isKindOfClass method if you want to invoke some methods of this class.

How can I pass a class name as an argument to an object factory in cocoa?

I am working on an object factory to keep track of a small collection of objects. The objects can be of different types, but they will all respond to createInstance and reset. The objects can not be derived from a common base class because some of them will have to derive from built-in cocoa classes like NSView and NSWindowController.
I would like to be able to create instances of any suitable object by simply passing the desired classname to my factory as follows:
myClass * variable = [factory makeObjectOfClass:myClass];
The makeObjectOfClass: method would look something like this:
- (id)makeObjectOfClass:(CLASSNAME)className
{
assert([className instancesRespondToSelector:#selector(reset)]);
id newInstance = [className createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
Is there a way to pass a class name to a method, as I have done with the (CLASSNAME)className argument to makeObjectOfClass: above?
For the sake of completeness, here is why I want to manage all of the objects. I want to be able to reset the complete set of objects in one shot, by calling [factory reset];.
- (void)reset
{
[managedObjects makeObjectsPerformSelector:#selector(reset)];
}
You can convert a string to a class using the function: NSClassFromString
Class classFromString = NSClassFromString(#"MyClass");
In your case though, you'd be better off using the Class objects directly.
MyClass * variable = [factory makeObjectOfClass:[MyClass class]];
- (id)makeObjectOfClass:(Class)aClass
{
assert([aClass instancesRespondToSelector:#selector(reset)]);
id newInstance = [aClass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
I have right a better tutorial on that , please checkout
https://appengineer.in/2014/03/13/send-class-name-as-a-argument-in-ios/
It's pretty easy to dynamically specify a class, in fact you can just reference it by it's name:
id string = [[NSClassFromString(#"NSString") alloc] initWithString:#"Hello!"];
NSLog( #"%#", string );
One other tip, I would avoid using the nomenclature 'managed object' since most other Cocoa programmers will read that as NSManagedObject, from Core Data. You may also find it easier to use a global NSNotification (that all your reset-able objects subscribe to) instead of managing a collection of different types of objects, but you're more informed to make that decision than I am.
The bit of the answer missing from the other answers is that you could define a #protocol containing your +createInstance and +reset methods.
It sounds like you want something like:
- (id)makeObjectOfClassNamed:(NSString *)className
{
Class klass = NSClassFromString(className);
assert([klass instancesRespondToSelector:#selector(reset)]);
id newInstance = [klass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
This would assume a class method named +createInstance. Or you could just use [[klass alloc] init].
To call it:
MyClass *variable = [factory makeObjectOfClassNamed:#"MyClass"];
Depending on what you're trying to do, it might be better to pass around class objects than strings, e.g.:
MyClass *variable = [factory makeObjectOfClass:[MyClass class]];