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, #"");
Related
I currently develop an SDK and I want to know how to access a list of variables through a class instance object as follow:
MyClass * myObject = [[MyClass alloc] init];
[myObject changeShape : myObject.FORM_SQUARE];
[myObject changeShape : myObject.FORM_CIRCLE];
[myObject changeShape : myObject.FORM_RECTANGLE];
...
These variables ( three dozen ) are static and return just an integer to identify the form.
Do I have to set a #property for each variable or may be there are a more optimized way?
If they are not properties then you will have to implement a getter method to access them.
-(String*)getiVar{
return iVar;
}
Finally, the solution is to use NS_ENUM.
Like that:
typedef NS_ENUM(NSUInteger, shape) {
rectangle = 0,
triangle = 1,
square = 1,
...
};
-(void)changeShape:(shape)newShape;
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]));
I have a class of type Matrix which I'd like to subclass. Let's call my subclass Column. I'd like to add new properties to my subclass as well. I love factory methods, and don't like to repeat code, so I create a factory method in my subclass which calls its superclass' factory method when initializing an object.
The problem is that if I create an instance of my Column class, and call a property only found in Column, I get an error, because my Column class' factory method which calls my Matrix class' factory method is returning an instance of type Matrix instead of Column or whatever subclass calls it. I understand why this happens, but I'm not sure how to fix this.
I've read the following article on factory methods, and know that instead of using [[Matrix alloc] init]; I should be using [[self alloc] init];. The problem is I'm not sure how I'd access my instance variables like matrix, rows and freeData which I define in my header as follows:
#interface Matrix : NSObject <NSCoding, NSCopying> {
#public double *matrix;
#public int rows;
#public int columns;
#private BOOL freeUpData;
}
Here's the factory method I need help in. How can I rewrite it so it can access my instance variables, and also work with subclassing (and not only return a Matrix instance).
+ (instancetype)matrixFromArray:(double *)arr Rows:(int)m Columns:(int)n Mode:(refMode)mode
{
Matrix *mt = [[Matrix alloc] init];
if (mode == YCMCopy) {
double *new_m = malloc(m*n*sizeof(double));
memcpy(new_m, arr, m*n*sizeof(double));
mt->matrix = new_m;
mt->freeData = YES;
}
else {
mt->matrix = arr;
mt->freeData = NO;
}
if (mode != YCMWeak) {
mt->freeData = YES;
}
mt->rows = m;
mt->columns = n;
return mt;
}
You always create Matrix instance
but you need to create instance of current class
+ (instancetype)matrix
{
Matrix *mt = [[self alloc] init];
....
return mt;
}
And if you do that
[Matrix matrix] will return Matrix object
[Column matrix] will return Column object
I want to write a method by the Objective-C that can be exported to JavaScript, so that I can get the JavaScript Object into the native code. For example in the following code: someObject is a native implemented object, and fun is its method.
someObject.fun({ k : 'value1', k2 : 'value2'})
I know that using JSExport can export the native method to JavaScript. and successfully pass the JavaScript String to the native code (NSString*). But when I want to pass the JavaScript Object to native, it fails.
How to solve this?
Thanks a lot
In addition to the conversion of javascript strings to NSString* as you have already observed, Javascript objects are copied to a KVC-compliant objective-C object (essentially, an NSDictionary instance) before being passed to your objective-c method.
For example, with the following definitions:
#protocol MyJSAPI
- (void)fun:(id)param;
#end
#interface MyObject: NSObject<MyJSAPI>
- (void)fun:(id)param;
#end
and the initialization of your JSContext as follows:
myjscontext[#"someObject"] = [[MyObject alloc] init];
[myjscontext evaluateScript:#"someObject.fun({ k : 'value1', k2 : 'value2'});
then your fun:(id)param method can access the k and k2 fields of the objects like this:
#implementation MyObject
- (void)fun:(id)param {
NSString* k = [param valueForKey:#"k"];
NSString* k2 = [param valueForKey:#"k2"];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
In addition to ste3veh answer.
You can use JSExportAs macro in following way to export Objective-C method as JavaScript one:
#interface MyObject: NSObject <JSExport>
JSExportAs(fun,
- (void)fun:(id)param
);
#end
#implementation MyObject
- (void)fun:(id)param {
NSString* k = [param objectForKey:#"k"];
NSString* k2 = [param objectForKey:#"k2"];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
In this case param would be implicitly converted to corresponding Objective-C oblect, using -[JSValue toObject]. In your case it will be converted in NSDictionary. To avoid implicit conversion, you may specify parameter as JSValue:
#interface MyObject: NSObject <JSExport>
JSExportAs(fun,
- (void)fun:(JSValue*)param
);
#end
#implementation MyObject
- (void)fun:(JSValue*)param {
NSString* k = [[param valueForProperty:#"k"] toString];
NSString* k2 = [[param valueForProperty:#"k2"] toString];
NSLog(#"k = %#, k2 = %#", k, k2);
}
#end
Note that big JavaScript objects, i.e. window or document, are very expensive to convert into corresponding Objective-C ones, so second way is preferred. Also, you can use JSValue parameter to call back JavaScript methods of this object from Objective-C, using callWithArguments:.
I don't think you can. See the JSExport.h header: It defines what the arguments can be, and it doesn't appear that generic JavaScript objects can be passed.
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.