what is the different between identifier id and class? - ios

id is the type of the object; the type could have been NSArray * or NSString *, for example.
Class can either be NSArray * or NSString * or other object.
I can use them like follow
- (void)wantWriteAction:(id)sender {
}
- (void)wantWriteAction:(Class)sender {
}
I want to know the different between them.In what conditions can't use id?what condition can't use Class?

id represents an instance of any class. Class represents any class-- not an instance, but the class itself.
So id could be a specific instance of NSString or NSArray. You can call instance methods on it-- those which are shown with a - in the documentation, like -length or -stringByAppendingFormat. If the id is an NSString then its value might be #"foo".
Class could be the NSString class or the NSArray class. You can call class methods on it-- those which are shown with a + in the documentation, like +stringWithFormat. If the Class is NSString (note, not an NSString, but the NSString class) then it has no string value, because it's not a string. It's a class.

Related

iOS invoke class method with IMP

Say I am developing a tweak app, I want to create an instance of one Class whose header can't be imported, but I do know class name, class methods and instance methods How can I create it in class method with parameters?
Say this Class called MMClass with class method
+(instancetype)do:(NSString*)string for:(NSString *)antherString;
What I am doing is as below:
Class class = objc_getClass("MMClass");
Method initMethod = class_getClassMethod(class,
#selector(do:for:));
IMP imp = method_getImplementation(initMethod);
id instance = imp(class,#selector(do:for:),#"do",#"ye");
Is this right?
First, I'm not sure if I'm stating the obvious, but why not just create your own header with the declarations you want to use and import that (or just inline the declaration at the top of your file if you are only going to use it in one file)? and call the methods normally? instead of going through all this mess? All the compiler cares about is that it sees some declaration for the methods you want to call.
When you call the actual method implementation using a function pointer, you need to cast it to the right type of function pointer corresponding to the signature of the method:
Class class = objc_getClass("MMClass");
Method initMethod = class_getClassMethod(class, #selector(do:for:));
IMP imp = method_getImplementation(initMethod);
id (*foo)(Class, SEL, NSString *, NSString *) =
(id (*)(Class, SEL, NSString *, NSString *))imp;
id instance = foo(class, #selector(do:for:), #"do", #"ye");
But it's silly to get an IMP that you are only going to use once. Rather, you should just cast objc_msgSend to the desired function pointer type, and call that directly instead:
Class class = objc_getClass("MMClass");
id (*foo)(Class, SEL, NSString *, NSString *) =
(id (*)(Class, SEL, NSString *, NSString *))objc_msgSend;
id instance = foo(class, #selector(do:for:), #"do", #"ye");

Customized NSLog to print all type of objects

I'm trying to extend NSLog that can print any type of object. Means, my custom class gets the value that need to be printed from the user and checks its class type and then it will print as desired format.
Consider my extended class name is, MyLog (So it contains MyLog.h and MyLog.m)
MyLog.h:
void MyLog (id objectValue);
MyLog.m:
#import "MyLog.h"
void MyLog (id objectValue)
{
if ([objectValue isKindOfClass:[NSString class]])
{
NSLog(#"%#",objectValue); //Same for NSArray, NSDictionary, NSMutableArray, NSMutableDictionary
}
else if ([objectValue isKindOfClass:[NSData class]])
{
NSLog(#"%#",[[NSString alloc]initWithData:objectValue encoding:NSUTF8StringEncoding]);
}
....
....
}
So, if I include this class (MyLog.h) in prefix file, I can call the below method from any class to simply print the given object.
MyLog(valueOfObject);
Problems:
The CGRect, CGpoint, CGSize related things (which are not an object) can not be passed as id to MyLog() function.
I tried to print the object name along with its value for more readability. Refer this post.
For example,
NSString *myString = #"Welcome";
MyLog(myString);
This will print myString: Welcome instead of just printing Welcome.
I can achieve this my defining a preprocessor like
#define MYLOG(x) NSLog( #"%s:%#",#x, x)
So, I tried to customize it in the following way
#define MYLOG(x) NSLog(#"%s:%#",#x, MyLog(x))
But it throws "Argument type 'void' is incomplete" error.
Questions:
How can I pass non objects as id to a function?
How can I call a function within a preprocessor macro?
Help needed!! Just Confused!!
If you implement -(NSString*)description you can print any object in NSLog with NSLog(#"%#", myObject).
So just add -(NSString*)description in the classes of objects that you want to print out, and in them just return the NSString value that you think is relevant for that object.

Why does this code sample copy before returning?

I'm looking at a code sample for deserializing a JSON response. The last line return [topics copy]; copies the array before returning. I've looked up the reason for this and it's to return an immutable NSArray.
However, is this standard practice or highly defensive programming? The calling method will assign the return value to something, and if it wants to assign the return value to an immutable NSArray it will do it. If it assigns the return value to an NSMutableArray then it will do that.
So my question is - is there any realistic scenario where this will prevent unwanted consequences?
// Returns array of #c NPTopic objects
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
return nil;
}
NSDictionary *JSONDictionary = [super responseObjectForResponse:response data:data error:error];
if (!JSONDictionary) return nil;
// Note: the expected JSON format of this response is { data: [ { <a topic> }, { <another topic>} ], metadata: { ...} }
NSArray *JSONTopics = JSONDictionary[#"data"];
NSMutableArray *topics = [NSMutableArray array];
for (NSDictionary *JSONTopic in JSONTopics) {
// For each topic in JSON format, we can deserialize it from JSON to our desired model class using Mantle
NPTopic *topic = [MTLJSONAdapter modelOfClass:[NPTopic class] fromJSONDictionary:JSONTopic error:error];
if (!topic) return nil;
[topics addObject:topic];
}
*error = nil;
return [topics copy];
}
The copy is so it returns an NSArray, not an NSMutableAray. The issue is that if an NSMutableAray is returned it can be changed, that can be a problem is there are multiple pointers to it and one makes changes but another assumes that it is immutable and won't change.
It is good practice.
Don't make assumptions about the actual implementation, there are several ways that the "copy" can occur without actually making a copy. Being concerned about performance without a need and proof is called" Premature Optimization" and warned again by many including famously by Donald Knuth.
It really should have it's return typed NSArray *, not id so the compiler can catch type errors.
Based on your comment to #Zaph, let's try for an explanation...
Fundamental to Objective-C, and many other languages, is the concept of subclasses; an instance of class B which derives from a class A can usually be used whenever an instance of class A is required.
Because of this there is a lot of type information loss; pass an instance of B to something expecting an A then the receiver doesn't know unless it chooses to query the actual type - the receiver has less information about the instance's actual type, seing it as an A, while the actual instance still is a B.
The extreme case of type information loss is when an instance is stored in a container, such as NSArray, which justs stores "objects" (id or NSArray *) - when that instance is later extracted little is known about it for certain, though if the programmer has only stored, say, NSString instances then they can safely assume only NSString instances are extracted.
All this usually works fine.
Where the "usually" breaks down is when some fundamental property, such as mutability, changes in the derived class.
Consider the simple class (please no sexism etc. comments!):
#interface Woman : Person
#property NSString *maidenName;
#property NSString *marriedName;
#end
#implementation Woman
// nothing to do
#end
and the code fragment:
Woman *mary = [Woman new];
NSMutableString *name = [NSMutableString stringWithString:#"Mary Jones"];
mary.maidenName = name;
[name replaceOccurencesOfString:#"Jones" with:#"Williams];
mary.marriedName = name;
What is the value of mary.maidenName? Mary Williams... Probably not what was intended.
What might be the result if rather can create your own string as above you obtained it from a method which claimed to return an immutable string and you assigned it to maidenName or marriedName, but in fact the return string was mutable and subsequently changed elsewhere? Poor Mary would find her name changing.
To address this problem two rules are generally advised:
Consumer: If you are storing a reference to an object of an immutable class which has a mutable subclass then copy the object before storing to avoid surprises if the instance is in fact mutable. For the above example this can be done by adding the copy attribute to the properties:
#property (copy) NSString *maidenName;
#property (copy) NSString *marriedName;
Producer: If you are creating and returning an object instance which you declare is immutable, but during creation you use a mutable subclass, then make an immutable copy and return that. I.e. return what you say you are returning (or an immutable subclass of it).
Following these rules should reduce surprises, and that is what the code for responseObjectForResponse does.
You are correct, this is defensive programming. However; due to the prevalence of type information loss, something fundamental to this style of programming, and the issues unexpected mutability can cause; it is not highly defensive programming.
As Monty Python, and others, would advise: always expect the unexpected and defend against it.
HTH

#encoding for type id where id is actually any object

I have a following method.
- (void)someObject:(id)obj {
char* encoding = #encoding(typeof(obj));
NSString *s = [NSString stringWithCString:encoding encoding:NSUTF8StringEncoding];
NSLog(s);
}
this method always return #"#" whether I pass a variable of type NSNumber, NSArray, NSDictionary or NSString in obj. I assume it is checking the obj pointer type.
What do I have to do so that it returns the actual type encoding of the variable that I have passed?
#encode() is a compile-time construct; it only knows about the variable's type, not the class of the object that will be contained in the object at runtime. Any object-typed variable will encode to #.
You will have to use runtime checks, asking the objects for their classes, to accomplish your goal.

Assignment of dynamic class type

I have a dictionary that contains a Class string name as the key and an array of corresponding class objects as the value.
It could be a number of different Class types that come in, so I wanted to do dynamic assignment at runtime.
Does anyone know why this code gives a compiler error?
// Where obj is an object of type MyClass
Class myClass = NSClassFromString(#"MyClass");
myClass *objectOfTypeMyClass = obj;
Update:
Here's how i ended up implementing it:
Class interestClass = NSClassFromString(classProvidedAsString);
id interest = [interestClass createNewInterestUsingManagedObjectContext:backgroundContext];
[interest setValue:title forKey:#"title"];
[interest addLikedByObject:aFriend];
Where title is a property on all objects that I can accept, and createNewInterest is a method all objects have.
The problem was trying to cast id as interestClass to use the properties and methods of that class.
Why just don't you use id?
id objectOfTypeMyClass = obj;
Another option would be use polymorphism (if the classes were created by you).
You can do this way:
NSObject *object=[NSObject new];//this can hold any kind of object
object=#"my name is lakhan";//as string
NSLog(#"%#",object);
object=#[#(10),#(50)]; //now working as array
NSLog(#"%#",object);
object=#{#"k1": #"10", #"k2":#"20"}; // as dictionary
NSLog(#"%#",object);
So as per your requirement you can use whatever value you need to set on the object.

Resources