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
Related
I know that I can override hash and isEqual to check 2 instances equality. Xcode has the default snippet and doucument https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/ObjectComparison.html as following
- (BOOL)isEqual:(id)other
{
if (other == self) {
return YES;
} else if (![super isEqual:other]) { //WHAT is this line mean ?
return NO;
} else {
return <#comparison expression#>;
}
}
- (NSUInteger)hash
{
return <#hash expression#>;
}
Okay,
other == self check two objects' pointers.
if ![super isEqual:other], what is this line means ? If super object is not equal other, return NO ? Then it will always return NO, the step 3 will not be executed.
Am I wrong ?
Thanks.
It's a typical implementation in a class-hierarchy, that is, if your class derives from a super class that has its own meaningful isEqual: implementation. In that case it is wise to let the super class test the equality of the common properties. If the common part is not equal, then there is no chance that the derived objects are equal.
It is not needed if you derive directly from NSObject.
Actually, you'll need an extra step as well:
- (BOOL)isEqual:(id)other
{
if (other == self) {
return YES;
} else if (![super isEqual:other]) {
return NO;
} else if (![other isKindOfClass:[MyClass class]]) {
return NO; // comparing incompatible objects
} else {
MyClass *myOther = (MyClass *) other;
return <#compare between self and myOther#>;
}
}
Let's look at one example of class inheritance:
#interface A : NSObject
#property (nonatomic, assign, readwrite) NSInteger fieldA;
#end
#interface B : A
#property (nonatomic, assign, readwrite) NSInteger fieldB;
#end
Now, if you want to implement equality on A, then you want to base it on the equality of fieldA:
// A equality
- (BOOL)isEqual:(id)other {
...
return [self fieldA] == [other fieldA];
}
When you are implementing equality on B, you need two conditions - first you have to make sure that fieldA is equal and then you have to make sure that fieldB is equal.
// B equality
- (BOOL)isEqual:(id)other {
...
return [super isEqual:other] && [self fieldB] == [other fieldB];
}
That's exactly what the [super isEqual:other] is doing - it checks the equality requirement of the superclass, that is fieldA.
To be honest, this isEqual: template is not very good. It is missing one of the most important things and that is the class equality check:
if (![other isMemberOfClass:[self class]]) {
return NO;
}
You don't need this check only when you never mix instances of different classes. However, when you start putting instances of A and B into the same array/dictionary etc. you will have crashes when trying to compare instances of A with instances of B.
There is a slightly different between Hash and isEqual in Objective-C.
First of all, NSObject checks equality with another object with the method isEqual: and basically, two objects may be equal to another, if they share a common set of observable properties.
Hashing in object comparison is an extra step in determining collection membership, which will faster your operation.
This will explain a little bit about hash and isEqual
Object equality is commutative ([a isEqual:b] --> [b isEqual:a])
If objects are equal, then their hash values must also be equal ([a isEqual:b] --> [a hash] == [b hash])
However, the converse does not hold: the hash values of two objects are equal do not mean that their values to be equal.
I hope this would be helpful. For reference, you can visit this link http://nshipster.com/equality/
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;
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 am using mogenerator to generate code from a model with a TestPerson managed object. TestPerson inherits from the abstract object TLSyncParent. In TLSyncParent I have the code:
- (void) awakeFromInsert
{
[super awakeFromInsert];
QNSLOG(#"%#\n%#", self.managedObjectContext, self.description);
if (self.syncStatus == nil) {
self.syncStatusValue = SYNCSTATUS_NEW;
self.tempObjectPID = [self generateUUID];
QNSLOG(#"After init values\n%#", self.description);
}
}
I create the TestPerson object in childMOC whose parent is mainMOC, whose parent is rootMOC. awakeFromInsert runs as expected and makes the init changes. When I save childMOC to mainMOC, awakeFromInsert is run again. From the docs I would not expect that, but there is some ambiguity. From the Docs, "You typically use this method to initialize special default property values. This method is invoked only once in the object's lifetime." The real problem is that when awakeFromInsert runs in mainMOC, the init changes made in childMOC are NOT there. awakeFromInsert is apparently run before the save actually takes place.
2013-10-02 11:22:45.510_xctest[21631:303] TestPerson -awakeFromInsert <NSManagedObjectContext: 0xd684780>
<TestPerson: 0xd6863b0> (entity: TestPerson; id: 0xd684ed0 <x-coredata:///TestPerson/t02B71E0D-AE3F-4605-8AC7-638AE072F2302> ; data: {
dept = nil;
job = nil;
objectPID = nil;
personName = nil;
syncStatus = 0;
tempObjectPID = nil;
updatedAt = nil;
})
2013-10-02 11:22:45.511_xctest[21631:303] TestPerson -awakeFromInsert After init values
<TestPerson: 0xd6863b0> (entity: TestPerson; id: 0xd684ed0 <x-coredata:///TestPerson/t02B71E0D-AE3F-4605-8AC7-638AE072F2302> ; data: {
dept = nil;
job = nil;
objectPID = nil;
personName = nil;
syncStatus = 4;
tempObjectPID = "7AB46623-C597-4167-B189-E3AAD24954DE";
updatedAt = nil;
})
2013-10-02 11:22:45.511_xctest[21631:303] CoreDataController -saveChildContext: Saving Child MOC
2013-10-02 11:22:45.511_xctest[21631:303] TestPerson -awakeFromInsert <NSManagedObjectContext: 0xd682180>
<TestPerson: 0xd68fce0> (entity: TestPerson; id: 0xd684ed0 <x-coredata:///TestPerson/t02B71E0D-AE3F-4605-8AC7-638AE072F2302> ; data: {
dept = nil;
job = nil;
objectPID = nil;
personName = nil;
syncStatus = 0;
tempObjectPID = nil;
updatedAt = nil;
})
2013-10-02 11:22:45.511_xctest[21631:303] TestPerson -awakeFromInsert After init values
<TestPerson: 0xd68fce0> (entity: TestPerson; id: 0xd684ed0 <x-coredata:///TestPerson/t02B71E0D-AE3F-4605-8AC7-638AE072F2302> ; data: {
dept = nil;
job = nil;
objectPID = nil;
personName = nil;
syncStatus = 4;
tempObjectPID = "B799AFDA-3514-445F-BB6F-E4FE836C4F9D";
updatedAt = nil;
})
What is the proper place to initialize a managed object when using the MoGenerator structure?
The documentation on awakeFromInsert is somewhat outdated and doesn't reflect the reality of nested contexts. When it says that the method is
Invoked automatically by the Core Data framework when the receiver is first inserted into a managed object context.
It should really say something like "..first inserted into any managed object context", since (as you've discovered) this happens more than once with nested contexts. Really, the notion of awakeFromInsert is kind of outdated when using nested contexts. The method was clearly designed in the old non-nested days and hasn't adapted.
There are a couple of ways to deal with this. One is a simple run time check, where you do something like:
if ([[self managedObjectContext] parentContext] != nil) {
// Set default values here
}
This code only runs when the current context is a child of some other context. The method still runs for the parent context, but you skip your default value setters. That's fine if you only ever nest one level deep, i.e. one parent with one or more child contexts, but no "grandchild" contexts of the parent. If you ever add another nesting level, you're right back where you started from.
The other option (and the one I usually prefer) is to move the default value code into a separate method, and then not use awakeFromInsert at all. That is, create a method called something like setDefaultValues, which in your case sets the values for syncStatusValue and tempObjectPID. Call this method right after you first create a new instance and nowhere else. Since it never gets an automatic call, the code never runs except when you tell it to run.
I am pretty sure Mogenerator doesn't change the way you create managed objects, but only moves the actual managed object classes to the machine generated files with the "_" prefix and creates subclasses of those managed objects to put all your custom logic in so that it doesn't get lost when you regenerated your managed object classes.
OK, thanks to Tom Herrington, I found a very nice way to do this. It seems to do exactly what I want with a minimum of trouble. It fits perfectly with the MoGenerator structure. I already had a category on NSManagedObject with the method initWithMOC. I added a call to the method awakeFromCreate and provided a default implementation. You just override awakeFromCreate in the same way you would override awakeFromInsert. The only requirement is that you ALWAYS create the MO using the initWithMOC method.
#implementation NSManagedObject (CoreDataController)
+ (NSManagedObject*) initWithMOC: (NSManagedObjectContext*) context
{
NSManagedObject* mo = (NSManagedObject*)
[NSEntityDescription insertNewObjectForEntityForName: NSStringFromClass(self)
inManagedObjectContext: context];
[mo awakeFromCreate];
return mo;
}
- (void) awakeFromCreate
{
return;
}
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.