In objective-c, NSCopying protocol provide us copy and mutableCopy. They all implement the copyWithZone: selector. Now, my requirement is:
For A : address is 0x00002bfa
A.name = #"John";
A.age = 10;
For B : address is 0x0000843d
B.name = #"Lily";
B.age = 20;
After A = [B customCopy];
Now A is:
For A : address is 0x00002bfa
A.name = #"Lily";
A.age = 20;
Only change the value of properties of A, the address still no change.
Cause it is custom NSObject. I think NSCopying could not work. Cause copy and mutableCopy will change the pointer of A, let it point to B. It is not my will.
Is it clear? Any idea! I am confused by the problem a long time. Thank you!
Try something like this
- (void) assignPropertiesFrom:(Object *) object {
self.name = object.name;
self.age = self.age;
}
[A assignPropertiesFrom:B];
It will repopulate A with property values of B, but A will still be the same object, so anyone holding pointers to it will have all the changes
Sergii's solution should work, but if you want to wander to uncharted teritories you could try something like:
#import <objc/runtime.h>
...
memcpy((__bridge void *)A, (__bridge const void *)B, class_getInstanceSize([A class]));
Related
I'm new to programming, and I feel a little intimidated posting, and I'm stuck. I don't want a quick fix! Please help me understand this.
I've created a custom method with a name, age, height and gender. It gets called when the NSMutableArray adds custom objects to the array. For some reason I cannot pull said items out of the NSMutableArray. Let's say the age needs to be printed out. I get a error saying...
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Person.m
- (id)initWithName:(NSString *)n
age:(int)a
height:(float)h
gender:(char)g
{
self = [super init];
if (self) {
self.name = n;
self.age = a;
self.height = h;
self.gender = g;
}
return self;
}
- (NSString *)description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:#"%#, %d, %.1f, %c",
self.name,
self.age,
self.height,
self.gender];
NSLog(#"Description String: %#", descriptionString);
return descriptionString;
}
When adding objects to the NSMutableArray they get converted to a NSString? How do I get the peoples age without the whole strings name and height in the NSLog?
ViewController.m
[self.people addObject:[[Person alloc] initWithName:#"Jake" age:29 height:73.5 gender:'f']];
[self.people addObject:[[Person alloc] initWithName:#"Jerry" age:24 height:82.3 gender:'m']];
[self.people addObject:[[Person alloc] initWithName:#"Jessica" age:29 height:67.2 gender:'f']];
NSString *mystring1 = [self.people objectAtIndex:0];
NSLog(#"%#", mystring1);
// Works
//NSString *list = #"Norm, 42, 73.2, m";
NSArray *listItems = [self.people[0] componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
Output
Description String: Jake, 29, 73.5, f
Jake, 29, 73.5, f
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Solved:
Notice the extra [ age];
int age = [[self.people objectAtIndex:0]age];
NSLog(#"%d", age);
I think the reason it seems like your object is getting converted to a string is this line,
NSString *mystring1 = [self.people objectAtIndex:0];
You need to specify the property if that is all you want to print
NSString *nameString = [[self.people objectAtIndex:0]name];
You can't perform componentsSeparatedByString on your Person object since it's not a string and doesn't have that method. (NSMutableArray objects are not automatically converted to NSStrings.) But to get the age of your Person, should be fairly simple anyway. Just access Person's age property:
int age = self.people[0].age;
NSLog(#"Age: %d", age);
Edit: You can technically use componentsSeparatedByString on your mystring1 NSString, like so:
NSArray *listItems = [mystring1 componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
But again, I don't see the point of doing this when you can access the Person's age property directly.
The componentsSeparatedByString method is an instance method of NSString class. You can't call it on your Person class in this way.
I don't know why you need that code, if you are trying to access age, then:
NSLog(#"Age %d", self.people[0].age);
is enough. If you are trying to achieve any other thing, then you can get the components like:
NSArray *listItems = [self.people[0].description componentsSeparatedByString:#", "];
NSLog(#"List Items: %#", listItems[1]);// age
One would think this would work; however, I can not call .age on my array.
NSLog(#"Age %d", self.people[0].age);
Gives the following output...
Property 'age' not found on object of type 'id'
One would also think componentsSeparatedByString would work. It doesn't split my string by the comma and gives an error.
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance
Solved READ UP
I think you are getting confused about why when you log the object you are getting a string, but you can't perform any methods on it.
And here is why you think you are getting a string, you've implemented the description property, which comes from the NSObject protocol. When you do a log of an object with a %# parameter you are getting the result of this property, which is a string.
So looking at your code:
NSString *mystring1 = [self.people objectAtIndex:0]; //1
NSLog(#"%#", mystring1); //2
The first thing is that you are getting the first object out of your array. objectAtIndex returns an id, which is a pointer to an NSObject. It is not a strongly typed return. You are allocating it to an NSString which is wrong, because it as actually a person object. But since you aren't calling any NSString methods on it, the compiler is not flagging this incorrect assignment.
The second thing is that you are just getting the result of the description property, as I've already mentioned.
Your edited solution:
int age = [[self.people objectAtIndex:0]age];
Works, but here is why: you are getting the objectAtIndex:0 which returns an id. Then you are sending it the message age. Objective-C is a dynamic language, which means you can send any message to any object (although you get a run-time crash if the object does not implement the method) In this case, your Person object does implement an age method (since it's a public property) so you get an age back.
A different way of doing it:
NSInteger age;
Person *person = self.people.firstObject;
if (person) {
age = person.age;
} else {
age = NSNotFound;
}
Why am I doing it this way?
Firstly, I am getting the firstObject out of the array and putting it into a typed variable. This is safer, as if there is no object at index 0 (i.e. the array is empty) then you won't crash your app. firstObject returns the first object if it exists, on a nil if it doesn't.
Secondly, Only if the person has been correctly extracted from the array, do I assign the age to an NSInteger variable. You should prefer the specific types of NSInteger over int (and CGFloat over float) because they will use the correctly sized variable when running on 32-bit or 64-bit` systems.
Thirdly, if person cannot be created I am assigned the value of NSNotFound to the age. This is a typedef for a very large number, one that you can compare against. Just returning 0 in age is not enough to tell you there was an error. The person could have an age of 0. with this code you can test the age with:
if (age != NSNotFound) {
// This is a valid age, do something with it
} else {
// A person object could not be extracted from the array, this is not a valid age
}
Is this overkill? Not really. When you start writing real, complex apps, this sort of defensive programming will come naturally to you. Arrays can be empty, your data could be incorrectly created, etc. Writing code that gracefully handles these eventualities is the real skill of programming. Unfortunately, It's a bit more long winded, and most tutorials that you find on the web show you the simple, happy path.
I hope this gives you a better idea of what your code is doing and what more you could be doing to write robust code.
I have a variable int and i want to save this to a PFObject. Whenever I try I get the error:
incompatible integer to point value from int to id.
I can add a normal int which is not in a variable by doing.
PFObject* object = [PFObject objectwithclassname:#"test"];
object[#"score"] = #30;
This works fine, and also I can add a string and it will work. It is when I try this that it does not work.
int test = 10;
object[#"score"] = test;
Anyone know?
The syntax is:
object[#"score"] = #(test);
Variables of type int are no (Objective-C) objects. You use the keyed subscription protocol for assignment. It only takes objects. Basically this is done:
[object setObject:test forKeyedSubscript:#"score"]; // Error: test is no object
So the solution is to put the non-object typed int into an object (boxing). You can do this with rmaddy's solution (boxed expression) or more explicit for a better understanding:
object[#"score"] = [NSNumber numberWithInt:test];
I would like to know if they have an equivalent in Objective C of the JAVA's methode "BeanUtils.CopyProperties(bean1, Bean2);" ?
Or other solution, i would like to cast motherObject to childObject :
#interface motherBean : NSObject{ ...}
#interface childBean : motherBean { ...}
motherBean m = [motherBean new];
childBean f = m;
With the first tests it's work but I have a warning : "incompatible pointer types returning ...";
I use WSDL2Objc and it generate bean, and the name of it can change between 2 generation :-/
I prefere to work with the child and just change the name in her definition
Thanks
Anthony
Take a look at commons-beanutils package. It has lots of property method for you to copy stuff. In particular:
PropertyUtils.copyProperties(bean1, bean2);
However, as to your second question, you're trying to downcast an instance of a parent class to a child class?
I'm not sure how that would be legal in any OO language. Sure you can forcibly cast:
// This is not legal because you can't case from one class to anther
// unless the actual instance type (not the declared type of the variable,
// but the constructed type) is either the casted class or a subclass.
Parent p = new Parent();
Child c = (Child) p;
But you'd get a ClassCastException , since you can't treat an instance of a parent class as if it were a child class (only the other way around). Either of these would be legal however:
// This is legal because you're upcasting, which is fine
Child c = new Child();
Parent p = c;
// This is legal because the instance "p" is actually an
// instance of the "Child" class, so the downcast is legal.
Parent p = new Child();
Child c = (Child) p;
To answer your first question, you could easily write the code to copy property values between instances. It is easiest if you restrict properties to proper Objective-C properties (items declared using #property()) which is probably the best practice anyway. You can use the Objective-C runtime functions to get the list of all properties on a class (class_getPropertyList) and call property_getName() to get the property's name and call property_getAttributes() to make sure it is writeable. Then you can use NSObject's Key Value Coding to get and set the property values using valueForKeyPath: and setValueForKeyPath: respectively.
Some problems with you code example are that instances should be pointers. Second, you need an explicit cast since you are assigning an instance of a class to its super class. The reverse would not require a cast. That is probably why you are getting the warning.
The method BeanUtils.copyProperties
//.h
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#interface BeanUtils : NSObject
+(void)copyProperties:(id)src dest:(id)dest;
#end
//.m
#import "BeanUtils.h"
#implementation BeanUtils
+(void)copyProperties:(id)src dest:(id)dest {
NSLog(#"classeSrc=%# dst=%#", [src class], [dest class]);
if(src == NULL || dest == NULL) {
return;
}
Class clazz = [src class];
u_int count;
objc_property_t* properties = class_copyPropertyList(clazz, &count);
NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count ; i++)
{
NSString *propertyName = [NSString stringWithUTF8String:property_getName(properties[i])];
[propertyArray addObject:propertyName];
//on verifie que la prop existe dans la classe dest
objc_property_t prop = class_getProperty([dest class], [propertyName UTF8String]);
if(prop != NULL) {
id result = [src valueForKey:propertyName];
[dest setValue:result forKey: propertyName];
}
else {
[NSException raise:NSInternalInconsistencyException format:#"La propriété %# n'existe pas dans la classe %#",propertyName, [dest class] ];
}
}
free(properties);
}
#end
call :
EleveBean *eleveBean = [EleveBean new];
eleveBean.nom = #"bob";
eleveBean.prenom = #"john";
tns1_EleveBean *tnsEleve = [tns1_EleveBean new];
[BeanUtils copyProperties:eleveBean dest:tnsEleve];
STAssertEquals(eleveBean.nom, tnsEleve.nom, #"");
STAssertEquals(eleveBean.prenom, tnsEleve.prenom, #"");
I'm working on a dynamic implementation of a dictionary that also supports properties declared using the #dynamic keyword (similar to NSManagedObject).
Can I tell at runtime if a particular selector was declared with #dynamic? Is this just compiler trickery for design time tooling and lost at runtime or is there someway to inspect this?
+ (BOOL) resolveInstanceMethod:(SEL)sel
{
NSString *method = NSStringFromSelector(sel);
// ideally I could also check here if the selector is #dynamic
if ([method hasPrefix:#"set"] && [method rangeOfString:#":"].location == method.length -1) {
class_addMethod([self class], sel, (IMP) dynamicSet, "v#:#");
return YES;
}
else if ([method hasPrefix:#"get"] && [method rangeOfString:#":"].location == method.length -1) {
class_addMethod([self class], sel, (IMP) dynamicGet, "v#:#");
return YES;
}
BOOL value = [super resolveInstanceMethod:sel];
return value;
}
Also, my class subclasses NSDictionary but it when [super resolveInstanceMethod:sel] is called for an existing method - it still returns false?
If you know the name of the property you can use some runtime functions to investigate whether it's a dynamic property or not, as shown in the following function. Make sure to import <objc/runtime.h>.
BOOL isClassPropertyDynamic(Class theClass, NSString *propertyName)
{
BOOL isDynamic = NO;
objc_property_t property = class_getProperty(theClass, [propertyName UTF8String]);
char *dynamicAttributeValue = property_copyAttributeValue(property, "D");
if (dynamicAttributeValue != NULL) {
isDynamic = YES;
free(dynamicAttributeValue);
}
return isDynamic;
}
However, it's not always going to be easy to go from a selector name to the property, as both getters and setters names can be customized at declaration time. Typically that is only done for getters of boolean properties but technically anyone can break that convention.
Conventionally, if a selector starts with "set" followed by an uppercase letter and contains one ":" at the end, the property name would be the string resulting from removing "set" and ":" and making the first letter lowercase.
If a selector starts with "is" followed by an uppercase letter and has no arguments, then the property name corresponding to that would be the string resulting from removing "is" and making the first letter lowercase. Selectors that have no arguments and don't start with "is" and an uppercase letter would generally have the property name and the selector name the same.
Again, that's just convention and will be broken by somebody somewhere. So, you have to decide if it's truly valuable to determine whether a selector corresponds to a dynamic property or not (like borrrden I doubt it's really relevant but I'm not familiar with your requirement).
You could also follow rob mayoff's excellent suggestion from the comments that you "iterate over all of the properties (using class_copyPropertyList ) and check the G and S (attributes) of each" to build a mapping between selectors and properties.
This question uses CLLocationCoordinate2D as an example, but this applies to other structs as well, such as CGPoint (although ones like those are usually automatically included).
I want to use CLLocationCoordinate2D as a return value in a class method. If it were an object you could write the following at the top and it would be fine, as long as the .m file had a reference to CoreLocation.h
#class ClassName
Is there an equivalent way of telling the compiler not to worry about the struct without re-declaring it or importing the header file into the class' header file?
I do not want to import CoreLocation.h into the header file, since that would mean every file that imports that header file would inherit CoreLocation.
Thanks
I'm not totally getting the point why you do not want to import CoreLocation, but CLLocationCoordinate2D is declared in CoreLocation.h. I'm not aware about a method like #class for struct and I don't think it exists since struct are C types.
What you can do is create your own class that wraps the CLLocationCoordinate2D or return the NSValue from it, or (why not?) a dictionary.
Easiest way to do this is to just use an object instead of the struct, then you can use the #class keyword. In this case, the CLLocation object works just fine. Alternatively you can often use an NSDictionary in place of a struct, but an object is a bit easier to manage.
You return a struct like any other type. But you should be aware that when returning a struct you are returning a copy of the internal value on the stack as a temporary variable. Unlike an Objective-C object where you are actually returning a pointer.
The type you return MUST be a complete type. That means, in your method declaration you need the definition of the struct. In your case, that means, you need to include the header.
For example:
typedef struct MyStruct {
int a;
int b;
} MyStruct;
#interface MyClass : NSObject
+(MyStruct) theStruct;
#end
#implementation MyClass
+(MyStruct) theStruct {
MyStruct s = {.a = 1, .b = 2};
return s;
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
MyStruct s1 = [MyClass theStruct];
s1.a = 100;
s1.b = 100;
NSLog(#"s1 = {%d, %d}", s1.a, s1.b);
NSLog(#"MyStruct.theStruct = {%d, %d}", [MyClass theStruct].a, [MyClass theStruct].b);
[MyClass theStruct].a = 0; // has no effect!
}
return 0;
}
Prints:
s1 = {100, 100}
MyStruct.theStruct = {1, 2}
There is no straightforward way of doing that with single keyword.
Here you can find why it is not straightforward, although it is stated that it is not possible to do that, somewhat true but not completely.
Forward declare a struct in Objective-C
And here is the workaround of doing this
Forward declare structs in Objective C
Hope this will help.