I have a UITableViewCell ProductsCell, I registered it in a nib , and dequeue to use it.
I want to set a property of it readonly. How to do it better?
Here is the code:
#property (nonatomic, strong, readonly) MyProductsVC * targetMyProductsVC;
- (MyProductsVC *)targetMyProductsVC{
if(!_targetMyProductsVC){
UIResponder *target = self.nextResponder;
do {
target = target.nextResponder;
} while (![target isKindOfClass: ZBMyProductsVC.self] && target != nil);
_targetMyProductsVC = (ZBMyProductsVC *)target;
}
return _targetMyProductsVC;
}
I can't put the implementation. in -init,-awakeFromNib , because I use UIResponder to find the parent ViewController.
Because in above methods, the cell seems like have not been added on the super view.
If I do like this ,
#property (nonatomic, strong, readonly) MyProductsVC * targetMyProductsVC;
Xcode reports:
Use of undeclared identifier '_targetMyProductsVC'
It is OK to set two property. one is inside like above , just set the other readonly property outside getter method (return the former property.)
It a little dirty ,
any better way ?
The code can work:
#property (nonatomic, strong, readonly) MyProductsVC * targetMyProductsVCReadOnly;
#property (nonatomic, strong) MyProductsVC * targetMyProductsVC;
- (MyProductsVC *)targetMyProductsVC{
if(!_targetMyProductsVC){
UIResponder *target = self.nextResponder;
do {
target = target.nextResponder;
} while (![target isKindOfClass: ZBMyProductsVC.self] && target != nil);
_targetMyProductsVC = (ZBMyProductsVC *)target;
}
return _targetMyProductsVC; }
- (MyProductsVC *)targetMyProductsVCReadOnly{
return self.targetMyProductsVC; }
Because you are implementing your own property getter for a readonly property, which means there is only one accessor (i.e. there is no setter), the compiler will not automatically declare the backing variable. From Encapsulating Data:
Note: The compiler will automatically synthesize an instance variable in all situations where it’s also synthesizing at least one accessor method. If you implement both a getter and a setter for a readwrite property, or a getter for a readonly property, the compiler will assume that you are taking control over the property implementation and won’t synthesize an instance variable automatically.
If you still need an instance variable, you’ll need to request that one be synthesized:
#synthesize property = _property;
You insert the above before your getter implementation to resolve the undeclared variable error.
HTH
Related
I am following the tutorial here: http://blog.soff.es/archiving-objective-c-objects-with-nscoding
to create an NSObject that can save my match data in a turn based game.
However I get this warning in my .m file:
Autosynthesized property 'title' will use synthesized instance variable '_title', not existing instance variable 'title'
So my Qustion is if (in the code below) I delete the code in between the brackets will I be losing something important?
#interface Note : NSObject <NSCoding> {
NSString *title;
NSString *author;
BOOL published;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *author;
#property (nonatomic) BOOL published;
#end
You shouldn't explicitly declare ivars since the properties will auto-synthesize their own ivars with slightly different names. The explicit ivars are pointless and won't be used by the properties. Having them is just going to lead to bugs when you use your ivars by mistake when you meant to set a property.
The warning is pointing this out by letting you know there will be two similar ivars.
Your code should simply be:
#interface Note : NSObject <NSCoding>
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *author;
#property (nonatomic) BOOL published;
#end
This avoid bugs such as:
title = #"Some Title"; // sets your ivar, not the property
as opposed to:
_title = #"Some Title"; // directly sets the property's ivar
Of course you should use the property:
self.title = #"Some Title"; // uses the property methods
You would be losing instance variables, but you don't really want them as the properties will create them for you (with slightly different names) and it's safer to access these (kind of hidden) auto-generated instance variables via the property accessor methods.
Yes it is and likely you get something you do not want.
If you do not have an instance variable neither with the identifier identifier nor with the identifier _identifier, manual synthesize and automatic synthesize will create one with the name _identifier.
If you already have an instance variable with the identifier _identifier, manual synthesize and automatic synthesize will use it. (Therefore in most cases it is meaningless to declare such an instance variable.)
But if you have an instance variable with the identifier identifier (without underscore) manual synthesize will use it, while automatic synthesize will not use it and instead create a new one with the identifier _identifier. Therefore you have two instance variables identifier and _identifier. Typically you do not want this.
I have 3 properties id_1, id_2, id_3
id_2 and id_3 are derived from id_1
id_1 can have public getter/setter
id_2 and id_3 only have readonly access.
So I need to override the setter for id_1 to set id_2 and id_3 for valid id_1
id_1 could come from NSUserDefaults which means in init, I need to set id_2 and id_3
So, I wanted to call setter of id_1 from init as if I was calling from outside of the class using ivar _id_1
That would give me a single implementation to set all the ids both during init phase or if called externally
My question is on following two lines that I have in my code as I am calling the setter for id_1 with argument as ivar _id_1
_id_1 = id_from_ns_user_defaults
[self setid_1:_id_1];
In few other SO articles I saw concerns around recursive loops
Custom Getter & Setter iOS 5
.h file
#interface UserCredentials : NSObject
#property (strong, nonatomic) NSString *id_1;
#property (readonly) NSString *id_2;
#property (readonly) NSString *id_3;
#end
.m file
#interface UserCredentials ()
#property (readwrite) NSString *id_2;
#property (readwrite) NSString *id_3;
#end
#implementation UserCredentials
- (id)init
{
self = [super init];
if (self) {
/* Is this valid in Objective-C */
_id_1 = id_from_ns_user_defaults
[self setid_1:_id_1];
}
return self;
}
- (void)setid_1:(NSString *)id
{
if (id && ![id isEqualToString:#""]) {
_id_1 = id;
_id_2 = convert2(_id_1);
_id_3 = convert3(_id_1);
}
}
#end
Your highlighted concern is around creating an assignment cycle. Because you are assigning to the ivar itself, you will not be creating a cycle. Remember that manipulating the ivar will not cause your getter/setter to be called -- it's just a pointer like any other pointer.
Setting an ivar to itself is not an issue unless you have done something in your setter implementation to make it an issue. In non-ARC systems, you could easily create a bad access error by implementing your setter with the wrong order:
- (void)setVal:(NSObject *)val {
[_val release];
_val = [val retain];
}
This is countered by using autorelease instead (or assigning to a temporary variable and releasing after the retain).
Most of the time, though, your setter won't be doing anything destructive when passed a new (or same) value. Your implementation does not do this.
Is there a way to define a setter method that will run on setting a property like:
i want to call
object.something = 0;
meanwhile in object class i want to achieve something like
- (void)setSomething:(NSInteger)something{
self.something = something;
// some other work too
}
What you want is called property.
you define property in class #interface like:
#interface MyClass()
#property (strong, nonatomic) SomeClass *object;
#end
It will automatically create ivar _object, setter and getter for it.
You can override accessor methods. But if you override both setter and getter, you need to synthesize property like:
#implementation MyClass
#synthesize object = _object;
//setter
- (void)setObject:(SomeClass *)object
{
_object = object;
}
//getter
- (SomeClass *)object
{
return _object;
}
//class implementation
#end
You can do it like this:
#property (nonatomic, weak, setter = setSomething:) UIImageView *photoImageView;
Anyway, setSomething: is the default method for a property named something. You just need to replace self.something with _something, as pointed in the comments.
I'm new to objective-c and found some OO features I have learned from other language is different in objective-c. The most confusing question for me until now is how to reimplement a property in subclass, or should I do this?
For example, I have two view controllers GameViewController and CardGameViewController.
#interface GameViewController : UIViewController {
Game *_game;
}
#property (nonatomic) Game *game;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (weak, nonatomic) IBOutlet UILabel *messageLabel;
#end
#interface CardGameViewController : GameViewController
#property (nonatomic) CardMatchingGame *game;
#end
CardMatchingGame is derived from Game. #property game in GameViewController is implemented like below:
- (Game *)game
{
if (!_game) {
_game = [[Game alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
}
return _game;
}
I tried to reimplement #property game like below but got a warning in the return clause which said Incompatible pointer types returning 'Game *' from a function with result type 'CardMatchingGame *'.
- (CardMatchingGame *)game
{
if (!_game) {
_game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]
matchThreshold:self.threshold.selectedSegmentIndex + 2];
}
return _game;
}
Is this the correct way of reimplementing a property in subclass? If not, what am I supposed to do?
You can't reimplement like that (well, you could do a ton of casting, but that isn't a good option).
One option is to create the property as #property (nonatomic) id game; and then it can hold any type of Game class (or subclass) that you have. And, because it is id type the compiler will assume that any method you try to call is valid. Obviously you could make mistakes which you won't find till later.
Another, better, option is to add a new property in the subclass as #property (nonatomic) CardMatchingGame *cardGame; and, in the subclass, when a new game is created the CardMatchingGame instance is stored in both game and cardGame properties. Now, the superclass has access to game and can use it, and the subclass has access to cardGame, which is typed as the correct class. So, you have compiler checks and usage of the same instance in both classes. But, you haven't reimplemented a property - you have multiple properties, each doing the appropriate task for their location + purpose.
If CardMatchingGame is a subclass of Game you can override - (Game *)game and return your CardMatchingGame object. Just make sure not to change the return type.
I think the problem is ivar in parent class Game *_game; it produce warning you see. Try to use different name or id type for it.
or just cast it before return (CardMatchingGame *)_game;
I made few classes via Core Data. And I need some additional #propertys for one of that classes in runtime. This #propertys are responsible for download progress and I don't want to store them in Core Data DB. I tried to use a separate extension class:
#interface MyClass ()
{
CGFloat _downloadProgress;
NSInteger _downloadErrorCounter;
BOOL _downloadAllStuff;
BOOL _downloadUserCanceled;
}
#property (nonatomic, assign) CGFloat downloadProgress;
#property (nonatomic, assign) NSInteger downloadErrorCounter;
#property (nonatomic, assign) BOOL downloadAllStuff;
#property (nonatomic, assign) BOOL downloadUserCanceled;
#end
But private variables are not visible out of MyClass, and #propertys compile all right, but in runtime i get -[MyClass setDownloadErrorCounter:]: unrecognized selector sent to instance.
Can anyone suggest me some solution?
The easiest solution (if you don't want to modify the Xcode generated class files) is to add the properties to the Core Data model and define the
properties as transient. Transient properties are not saved to the store file.
Another option is to use a tool like "mogenerator", which generates two class files for each
entity, one for the Core Data properties (which is overwritten if the the model changes),
and one for your custom properties (which is not overwritten).
Update: Starting with Xcode 7, Xcode creates both a class and
a category for each managed object subclass, compare NSManagedObject subclass property in category. Custom properties can be added to the class
definition which is not overwritten when the model changes.
Just add
#synthesize downloadErrorCounter = _downloadErrorCounter;
...
in #implementation. Note, not #dynamic.
When trying to use the #synthesize solution i got the error:
#synthesize not allowed in a category's implementation.
Solution was to use associated objects as described in this blog: http://kaspermunck.github.io/2012/11/adding-properties-to-objective-c-categories/
MyManagedObject+Additions.h
#property (strong, nonatomic) NSString *test;
MyManagedObject+Additions.m
NSString const *key = #"my.very.unique.key";
- (void)setTest:(NSString *)test
{
objc_setAssociatedObject(self, &key, test, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)test
{
return objc_getAssociatedObject(self, &key);
}