How to run getter methods when adding objects to array? - ios

Basically I'm trying to implement a getter and a setter for my mutable array.
The getter gets called fine, however the setter is apparently only called when I directly set the array equal to something (using =).
However the setter is not called when I add an item to the array using the code below:
[self.HighScores insertObject:newScore atIndex:i];
I see that there is a bunch of extra methods being "suggested" by xcode such as:
-(void) insertObject:(NSObject *)object inHighScoresAtIndex:(NSInteger)index
However having added it it still doesn't get called.'
Parts of my code are listed below:
HighScoreCollection.h:
#interface HighScoreCollection : NSObject
#property(nonatomic) NSMutableArray *HighScores;
- (bool) AddHighScore: (HighScore* )newScore;
- (NSMutableArray*) HighScores;
- (void) setHighScores:(NSMutableArray*)HighScores;
#end
HighScoreCollection.m:
#implementation HighScoreCollection
#synthesize HighScores = _HighScores;
- (void) setHighScores:(NSMutableArray*)HighScores
{
//setter code
}
- (NSMutableArray*) HighScores
{
//getter code
}
#end
How do I run a setter when I call array methods such as insert object etc. ?

Add a class
Inherit from NSObject
Inside the object from step 2 own a NSMutableArray in this custom object (composition design pattern link).
After that, call your custom setter in your new composite object when u want to.
Note: Your design is wrong, and you shouldn't thinks how the NSMutableArray insert object implement.

Related

Why are class objects the property attribute of retain and not copy? [duplicate]

This question already has answers here:
Objective-C declared #property attributes (nonatomic, copy, strong, weak)
(4 answers)
Closed 6 years ago.
I was trying to pass a custom object to the next view controller and I encountered this error -[ClassName copyWithZone:] unrecognized selector sent to instance
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"attemptDetails"])
{
ResultsVC *vc = segue.destinationViewController;
vc.selectedEntry = selectedEntry;
}
}
#property (nonatomic, retain) ClassName *selectedEntry; //Why is it retain and not copy?
I'm still very confused with property attributes and why certain types use certain attributes, like NSString uses (nonatomic, copy) and CLLocationCoordinate2D uses (nonatomic, readonly).
Could someone explain or link a reference to me how each property attribute works? Much thanks!
There are lots of descriptions for property attributes explanation,
Reference links,
Objective-C ARC: strong vs retain and weak vs assign
https://stackoverflow.com/a/4511004/4294543
#property and retain, assign, copy, nonatomic in Objective-C
Short & simple my understanding is like,
retain : It's working on the created object, and it just increase the reference count.
Here in your case you have already model class object so not need to copy in the second vc property,you just need to retain it to second vc property.
copy : The value you assigned to property can be copied & used for other purposes too(create shallow copy of object & need when object is mutable & need to release after finish with it).
nonatomic : Thread access is faster but you can't simultaneously access & change your property.
readonly : You can't directly assign the property new value.
Even i have run your case in the my project,
#import "ViewController.h"
#import "TestViewController.h"
#import "CustomClass.h"
#interface ViewController (){
CustomClass *classT;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
classT = [[CustomClass alloc]init];
classT.test = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btn:(id)sender {
TestViewController * vc = [self.storyboard instantiateViewControllerWithIdentifier:#"TestViewController"];
vc.className = classT;
[self presentViewController:vc animated:YES completion:nil];
}
#end
#import <UIKit/UIKit.h>
#import "CustomClass.h"
#interface TestViewController : UIViewController
#property (nonatomic,retain) CustomClass *className; // Work as i said
//#property (nonatomic,copy) CustomClass *className; // Makes a copy of an object, and returns it with retain count of 1. If you copy an object, you own the copy. This applies to any method that contains the word copy where “copy” refers to the object being returned thats why here you will get crash
#end
I have read couple of good article for memory management. According to rypress
Retain Attribute : The retain attribute is the Manual Retain Release version of strong, and it has the exact same effect: claiming ownership of assigned values. You shouldn’t use this in an Automatic Reference Counted environment.
Copy Attribute : The copy attribute is an alternative to strong. Instead of taking ownership of the existing object, it creates a copy of whatever you assign to the property, then takes ownership of that. Only objects that conform to the NSCopying protocol can use this attribute.
Even I went through some good link of stackoverflow as well. Joshua Nozzi's answer gave good explanation for retain vs copy.
Retain vs. Copy - Declared properties use retain by default (so you can simply omit it altogether) and will manage the object's reference count automatically whether another object is assigned to the property or it's set to nil; Use copy to automatically send the newly-assigned object a -copy message (which will create a copy of the passed object and assign that copy to the property instead - useful (even required) in some situations where the assigned object might be modified after being set as a property of some other object (which would mean that modification/mutation would apply to the property as well).
Also found good example here.
Code :
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#"First",#"Second", nil];
NSMutableArray *copiedArray = [array mutableCopy];
NSMutableArray *retainedArray = [array retain];
[retainedArray addObject:#"Retained Third"];
[copiedArray addObject:#"Copied Third"];
NSLog(#"array = %#",array);
NSLog(#"Retained Array = %#",retainedArray);
NSLog(#"Copied Array = %#",copiedArray);
Output :
array = (
First,
Second,
"Retained Third"
)
2013-12-19 17:15:49.380 RetainVsCopy[2876:c07] Retained Array = (
First,
Second,
"Retained Third"
)
2013-12-19 17:15:49.381 RetainVsCopy[2876:c07] Copied Array = (
First,
Second,
"Copied Third"
)
See, both array and Retained Array are having same contents. This is because both are pointing to same memory/instance/object. Where as contents of Copied Array are different. This is because copy created a separate instance.
In Objective C you will find that each class actually has a structure behind it. The properties are shortcuts which create the value in structure, a getter and a setter. For instance:
#interface MyClass
#property id myValue;
#end
Will create:
#interface MyClass {
id _myValue;
}
#property id myValue;
#end
#implementation
- (id)myValue {
return _myValue;
}
- (void)setMyValue:(id)myValue {
_myValue = myValue;
}
#end
Now these flags such as retain and copy add additional logic to the setters and getters. Using copy will actually create a setter as:
- (void)setMyValue:(id)myValue {
_myValue = [myValue copy];
}
Which means that the value must have the copy method implemented. Since your object does not it crashes.
Why to use copy is for safety. This is rarely important for something as strings but it is important for something like an array. So for instance you create a property #property NSArray *myArray; which expects an un-mutable array but the problem is that you can set a mutable array as well: myClassInstance.myArray = [[NSMutableArray alloc] init];. Now 2 modules have the access to the same mutable array. So if the first object starts modifying the array while the other one expects the array to always be the same you may find some issues. For instance MyClass instance may use it as a data source for the table view and at some point the array is mutated but the cells are not added/removed and the table view will cause a crash.
To be honest you can simply leave all of these as default and modify them only when you really need to. The case like above is highly unlikely anyway.

Handling undefined keys in Core Data

Is it possible to set a handler for undefined keys in Core Data?
I'm asking because despite defining valueForUndefinedKey: my implementation of that method is never called if valueForKey: is invoked on a managed object that doesn't have an attribute with that key.
This is needed for a synchronization system I'm currently writing where an object can be marked as locallyCreated or locallyDeleted but at the same time not all objects are editable so I want to avoid defining these properties for all entities in my model (around ~25 entities).
Although it seems tempting to create a single parent entity for that purpose I would like to avoid doing that since that will put all objects in one giant SQLite table which as far as I know will have negative impact on performance.
Currently I have a base "entity" class called RemoteObject that defines some common attributes like remoteID, locallyCreated, locallyDeleted, as suggested in another answer on SO, which all other entities inherit in code like this:
#interface RemoteObject : NSManagedObject
#property (nonatomic) NSString *remoteID;
#property (nonatomic) BOOL locallyCreated;
#property (nonatomic) BOOL locallyDeleted;
#end
#implementation RemoteObject
#dynamic remoteID;
#dynamic locallyCreated;
#dynamic locallyDeleted;
#end
#interface Project : RemoteObject
// custom properties
#end
What I want is to inspect any given RemoteObjet and see if it was locally create or deleted. However, as I said above, not all of the entities have corresponding attributes, so Core Data will throw an exception.
I found a workaround that allows me to avoid those errors - define a class method instead:
- (id)valueForKeyIfExists:(NSString *)key {
if (self.entity.attributesByName[key] != nil) {
return [self valueForKey:key];
}
return nil;
}
+ (BOOL)objectIsLocallyCreated:(RemoteObject *)object {
return [[object valueForKeyIfExists:#"locallyCreated"] boolValue];
}
But I was wondering if it would be possible to refactor this into object properties instead, catching undefined keys with valueForUndefinedKey: like this:
- (id)valueForUndefinedKey:(NSString *)key {
if ([key isEqualToString:LocallyCreatedKey]
|| [key isEqualToString:LocallyDeletedKey]) {
return #(NO);
}
return [super valueForUndefinedKey:key];
}
- (BOOL)locallyDeleted {
return [[self valueForKey:LocallyDeletedKey] boolValue];
}
It would be better to move those 2 flag attributes into a different class and then anything which doesn't have them is a subclass of RemoteObject and anything which does is a subclass of the new class, perhaps TrackedRemoteObject. Then in your algorithm you can class test to determine conformance.

Objective-C properties and instance variables

Problem
I want to create an interface with this signature, but without auto-synthesized instance variables:
#interface MyObject : NSObject
#property (nonatomic, copy) NSArray *values;
#end
Question:
Is it possible to prevent instance variable to be auto synthesized in .m #implementaion, as I want to implement my own getter and setter and I'm not going to use instance variable.
Reason:
The reason is that I don't want to have memory overhead, as data is going to be stored in plain bytes archive. At the same time I don't want users to know implementation issues and keep interface signature unchanged.
#implementation MyObject {
NSData *_data
{
- (NSArray *)values
{
// Generate NSArray from _data
}
- (void)setValues(NSArray *)values
{
// Set _data from values
}
#pragma mark - Hidden init
- (id)initWithData:(NSData *)data
{
// Set _data
}
#end
If you implement both getter and setter yourself, instance variables are not synthesized.
As others said - if you override the setter and getter - the compiler does't do anything else. So what you want.. is what you have typed out.
If you dont wantbto create just create only instance variable.
#interface MyObject : NSObjet
{
NSArray *values;
}
#end

Detect parent class variable or property is changed by other child class in objective-c

I have parent UIViewController A which has some properties. And I have child UIViewController B,C,D which are inheriting from A.
My question is:
In the B, I change Parent properties value. How can C,D know Parent value is changed from B?
Basically I want to share properties between children.
Thanks in advance.
Code example;
#interface A : UIViewController{
NSString *string1; //This is just example
}
#property (nonatomic, strong) NSString *string2;
#end
Child B
#interface B : A
#end
#implementation B
//here I change string1 and string2;
#end
Child C
#interface C : A
#end
#implementation C
//here I want to get changed string1 and string2 by Child A;
#end
Child D
#interface D : A
#end
#implementation D
//here I want to get changed string1 and string2 by Child A;
#end
At the moment, I am using Singleton to store all the value and It is working well, but I think there should be better way in such situation?
I guess that the simplest thing that you can do is have the property that is inherited, but override the getter and setter in the parent view so that it read/write to a static variable.
#interface A : UIViewController
#property (nonatomic, strong) NSString *sharedString;
#end
#implementation A
static NSString* _sSharedString;
- (NSString*) sharedString {
return _sSharedString;
}
- (void) setSharedString: (NSString*) aString {
_sSharedString = aString;
}
#end
You may wish to synchronize the property.
This, thinking out of the code, will change the value for all children. I understood that that was what you needed (only value sharing), but if you also need a notification when the value changes you could add some method that notifies children (instances) whenever value changes.
You could have the parent class send a message when its properties change. Subclasses could implement a method for that message, and in it do whatever they have to do to adapt to the change.
I seem to remember something like this already being built in to Cocoa though...
Ahh. Here it is. Look into Key-Value Observation. Cocoa will automatically send messages when a property changes, as long as the properties are KVC compliant. You just have to implement a method in the child to handle them, and subscribe to the change events.
So in your child class, you'd say something like
[self addObserver:self
forKeyPath:#"thePropertyName"
options:NSKeyValueObservingOptionNew
context:nil];
and have a method like
-(void) observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)observee
change:(NSDictionary*)change
context:(void*)context
{
// here's where the magic happens
}
keyPath will be the name of the property that changed, and change will contain the new value under a certain key with a constant name (the name's NSKeyValueChangeNewKey, no quotes). (You can generally just look up the new property as well.)
If there will be a lot of properties to watch, this might get cumbersome for each child to subscribe to each property's changes. How you'd handle that depends on how exactly everything's related, but eh. Having to care so much about the parent is kind of a smell in itself...

Override a method in Objective c via category

The following is working in objective c:
// Base Class in ClassA.h and ClassA.m
#interface ClassA : NSObject
- (NSString *) myMethod;
#end
#implementation ClassA
- (NSString*) myMethod { return #"A"; }
#end
//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
#interface ClassA (CategoryB)
- (NSString *) myMethod;
#end
#implementation ClassA (CategoryB)
- (NSString*) myMethod { return #"B"; }
#end
The question is, if I am just importing ClassA.h and send the message
[myClassA myMethod]; //returns B
why is this returning B? I am not importing ClassA+CategoryB
Even futhrer, if I did the following:
// Base Class in ClassA.h and ClassA.m
#interface ClassA : NSObject
- (NSString *) myMethod;
- (NSString *) mySecondMethod;
#end
#implementation ClassA
- (NSString*) myMethod { return #"A"; }
- (NSString *) mySecondMethod { return [self myMethod]; }
#end
//Category in ClassA+CategoryB.h and ClassA+CategoryB.m
#interface ClassA (CategoryB)
- (NSString *) myMethod;
#end
#implementation ClassA (CategoryB)
- (NSString*) myMethod { return #"B"; }
#end
and call mySecondMethod:
ClassA *a = [[ClassA alloc] init];
NSLog(#"%#",[a myMethod]);
the result will still be B although nobody knows (due to no import) of the category implementation?!
I'd excepted, only to return Bif I was importing the category...
So any hints appreciated.
Objective-C messaging is dynamic, this means that it doesn't matter if you import or not the category. The object will receive the message and execute that method.
The category is overriding your method. This means that when the runtime sends a message to that object, will always find the overridden method, no matter what you import.
If you want to ignore a category you shouldn't compile it, so you could remove the category from the compiler sources.
An alternative is subclassing.
Also read this:
Avoid Category Method Name Clashes
Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.
So in your case you haven't got any problem because as stated, this is less likely to happen with user defined classes. But you should definitely use subclassing instead of writing a category.
Obj-C allows you to add methods to an existing class using Categories. So if you add a method to NSString, the categoriesed method is available to NSMutableString and all classes which inherits NSString or any subclasses of NSString.
But you should avoid to Override the category.
You will not be 100% sure which method will be called. It depends on compiler.
From Apple Documentation.
Although the Objective-C language currently allows you to use a category to override methods the class inherits, or even methods declared in the class interface, you are strongly discouraged from doing so. A category is not a substitute for a subclass. There are several significant shortcomings to using a category to override methods: When a category overrides an inherited method, the method in the category can, as usual, invoke the inherited implementation via a message to super. However, if a category overrides a method that exists in the category's class, there is no way to invoke the original implementation. A category cannot reliably override methods declared in another category of the same class. This issue is of particular significance because many of the Cocoa classes are implemented using categories. A framework-defined method you try to override may itself have been implemented in a category, and so which implementation takes precedence is not defined. The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes.
You implicitly included the code defined in the category by compiling it.
If you want to avoid the category code to be executed you should remove it from your target, by removing the category implementation file.
You can do that from
Target->Build Phase->Compile Sources
That said, you should never use a category to override a method. That's a very bad practice and it's not what categories are for.

Resources