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);
}
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.
Can I replace an property with new one using some obj-c runtime features.
So I have a class A which contains a property:
#property (nonatomic, strong) Status *status;
So I want to inherit from this class like ClassB : ClassA and have ability to switch original #property (nonatomic, strong) Status *status; to my new property like #property (nonatomic, assign) NSInteger status;
So the reasone why I needed because I don't want to have a full copy of class A which contains 20 properties, so I just want to inherit from it and replace one with needed type.
Not sure if this possible, but I know something like swizzling and some obj-c runtime features can make a magic in the code.
I want to declare a public NSString property in my class which acts as a readonly property outside my class but i can assign any value to it inside my class. How can i achieve this behavior.
You have to declare your property in the .h file like this
#interface MyClass : NSObject
#property (strong, nonatomic, readonly) NSString *aString;
#end
but in your .m file you have to have
#interface MyClass () // your anonymous category
#property (strong, nonatomic, readwrite) NSString *aString;
#end
#implementation MyClass
#end
Externally the aString is readonly and internally you can set the value (readwrite).
You are achieving it by implementing a anonymous category also known as class extension in Objective-C
Define the property as readonly in the header and declare it readWrite in the implementation file in a class extension. The property will be readonly outside the classs implementation and read/write in the implementation.
// Interface file:
#interface Test : NSObject
#property (nonatomic, copy, readonly) NSString *propertyString;
#end
// Implementation file:
#interface Test () // Class Extension
#property (nonatomic, copy, readwrite) NSString *propertyString;
#end
#implementation Test
#end
See: Use Class Extensions to Hide Private Information
As #Amin Negm-Awad points out in an answer: the interface and class extension do not need to be in an interface or implementation file albeit this is the usual usage.
In .h file add:
#property(nonatomic,readonly)NSString* property;
In .m file add:
#interface yourClass ()
#property(nonatomic,readwrite)NSString* property;
#end
Define the property as readonly in your header file (interface), and as readwrite in your implementation file. That also allows you easily to make it weak / strong / copy.
This might be quit obvious:
in your .h file declare property as readonly
#property (nonatomic, assign, readonly, getter = isLoading) BOOL loading;
in your .m file declare property as readwrite
#property (nonatomic, assign, readwrite, getter = isLoading) BOOL loading;
This is an example, obviously you should create strong NSString property, and I assume compiler won't allow to set other value outside the class, but inside it will.
Beside the existing answers that told you to define a readonly property and change it to a readwrite property, which is completely correct and the intended pattern (that is, what readwrite is for), I want to add an probably important information:
You put the readonly definition in the interface. (Not header!)
You put the readwrite definition in a class continuation. (Not implementation file)
One might say that this is the same, because interfaces reside in the header and class continuations reside in the implementation file. But this is only the usual case.
You can additionally put class continuations in a third file. Then something like a "friend class" can import it additionally and this "fried class" has write access. I do that very often, when developing frameworks.
MyClass.h: // public header, available for everybody
#interface MyClass : NSObject
#property (readonly, …) id property1; // Everyone can read it
#property (readonly, …) id property2; // Everyone can read it
- (void)method; // Everyone can use it
#end
MyClass_Package.h: // project header, available for framework classes, unavailable for the user of the framework
#interface MyClass()
#property (readwrite, …) id property1; // All classes inside the framework can write it
- (void)packageMethod; // All classes inside the framework can use it
#end
MyClass.m
#interface MyClass() // A second class extension inside .m
#property (readwrite, …) id property2; // Only MyClass can write it
- (void)privateMethod; // Only MyClass can use it
#end
Define the property as readonly in the header, and set it using the underscore syntax.
#property (nonatomic, readonly) NSString *myString;
- (void)someMethodInYourDotMFile {
_myString = YES;
}
I'm new to objective-C, so apologies if this is repeated somewhere. I have a category(?) that is something like:
inside SomeClass.h:
#interface SomeClass (SomeCategory) <SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#property (nonatomic, retain) id someProperty; // <-- i want to move this to "private"
#end
and now in my SomeClass.m, all i have is:
#implementation SomeClass (SomeCategory)
// dynamic setters/getters here for someProperty.
#end
I think the someProperty is public. how do i make this "private"? (in other words, how do i syntactically put this in the .m file? i tried to use
#interface SomeClass (SomeCategory) {
#property (nonatomic, retain) somePrivateProperty;
}
#end
but it just complains that i have duplicate definition of the category. how do i do this correctly?
In your .h file, you should not give the category. Just use:
#interface SomeClass : SomeBaseClass < SomeDelegate>
#property (nonatomic, retain) id somePublicProperty;
#end
In your .m file, define your private property inside a class extension:
#interface SomeClass ()
#property (nonatomic, retain) id somePrivateProperty;
#end
A class extension is not a like category in that it allows you to extend an interface as well as add new storage to your class.
In a class category, you can define new properties, but no storage will be allocated for it, so you have to do it by hand:
#interface SomeClass (SomeBaseCategory)
#property (nonatomic, retain) id somePrivateProperty;
#end
#implementation SomeClass {
id _somePrivateProperty;
}
- (void)setSomePrivateProperty:(id)property {
_somePrivateProperty = property;
}
- (id)somePrivateProperty {
return _somePrivateProperty;
}
#end
Otherwise your app will crash.
In any case, keep in mind that given the dynamic nature of Objective-C, your property will never be fully private, since you can always send a message to an Objective-C object through objc_msgsend and thus set or read the property value.
EDIT:
If you do not have the source code for a class implementation, you cannot define a class extension (as per source linked above).
In this case, you could use object association to define properties.
Just add the category definition in the .m file OUTSIDE the implementation block
Like so:
#interface MyClass (MyCategory)
#property (assign) BOOL myPrivateProperty;
#end
#implementation MyClass
...
#end
Categories are best used for adding capability to code you do not own and cannot change. Adding properties via categories is not impossible, but is much more difficult.
Class Extensions are best used for keeping properties your object needs, but are not intended to be public.
If you do truly need to add properties to this object, the way to do it is with the Objective-C runtime's associated objects
There's an excellent writeup of when/how to use them here
I am downloading a list of objects from an API to display to a user. The list has a mix of two types of objects. Imagine that they are combined books and authors, and the class definitions look like this:
#interface Book : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) Author *author;
#end
#interface Author : NSObject
#property (nonatomic, strong) NSString *fullName;
#property (nonatomic, weak) Book *book;
#end
Every Book can download its Author information from the API, and vice versa.
If the API gives me a Book, I can set its author property once I download it. The Author object points back to the Book through the book property, but this doesn't create an ARC Retain Cycle because the book property is weak.
However, if the API gives me an Author first, and I download its Book, the object will be deallocated once the method in which I set it returns, because the same property is weak.
I thought of a few ways around this:
Create a Content object that stores both (not viable for many-to-many relationships)
Create separate strongBook and weakBook properties, and then make a readonly property called book which checks which is set and returns that one
Those both seem messy to me, although the second option is preferable.
Is there a way to dynamically change a property from weak to strong (and vice-versa) using the Objective-C runtime?
UPDATE: I'm getting a few suggestions on how to work around the issue, which I don't have trouble coming up with myself. This question is specifically about whether there is a way to either (a) dynamically redefine #properties for a specific instance of a class, or (b) override ARC's retain/release behavior in specific circumstances (since this issue wouldn't exist in MRC).
Just a shot in the dark, but you could create the property and not specify and then use dynamic with the runtime apis. I didn't test it, but i think it should work:
//.h file
#import <Foundation/Foundation.h>
#interface SomeObject : NSObject
#property(nonatomic) NSObject *object;
#end
//.m file
#import "SomeObject.h"
#import <objc/runtime.h>
#implementation SomeObject
#dynamic object;
-(void)setObject:(NSObject *)object
{
BOOL isWeak = NO;
if(isWeak)
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
}
else
{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_RETAIN);
}
}
-(NSObject *)object
{
return objc_getAssociatedObject(self, "object");
}
#end
For the period of the download, create a mutable dictionary to temporarily store author objects that arrive prior to the book. When a book is received, look in that array and see if the author info is there, if so attach it. When you are finished clean out the mutable array.