I am on objective-c, macOS.
I have an options Dictionary declared like this
#property NSMutableDictionary* options
Now the object containing the dictionary needs to be copied with copyWithZone including the options.
I can invoke "copy" on the dictionary. But how do i make sure that every object added to the dictionary (which basically can be any kind of object), complies to the NSCopying Protocol?
Something like
#property NSMutableDictionary* <NSCopying> options
Can i add something to the property declaration (that i didn't find out yet) or do i need to subclass NSDictionary (which i most likely want to avoid)
Regards
Pat
Related
Consider a C++ API like const T* foo(). This clearly documents the supported mutability and use of the API: OK, we'll let you look at T, but please don't change it. You can still mutate it, but you have to explicitly use const_cast to indicate your intention to not follow the API.
A good portion of Objective-C API's are comprised of property declarations. How is a user of an API supposed to interpret: #property (readonly) T foo ? (Assume T isn't an immutable type)
Since the setter isn't synthesized, clearly foo isn't mean to be replaced.
However, the getter still gives me a pointer to foo. Is it safe to mutate foo? (Clearly I can)
NOTE: I'm not asking about the language specs. I'm asking about what the conventional interpretation of an API like this is within the Objective-C community.
As matt said, the fact that you've got a pointer to the object does not mean that the object itself is mutable. Objective-C uses the behavior of the class, not the pointer, to enforce immutability. So in general you should be seeing read-only properties that return, e.g., NSString rather than NSMutableString.
I took a look through Apple's iOS framework headers to verify this:
grep -rn "#property.*readonly.*Mutable" /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/*.h
(The pattern for class names in Cocoa is to call the mutable "version" of the class $PREFIXMutable$CLASSNAME: NSString/NSMutableString, NSDictionary/NSMutableDictionary.)
./System/Library/Frameworks/AVFoundation.framework/Headers/AVComposition.h:133:#property
(nonatomic, readonly) NSArray<AVMutableCompositionTrack *> *tracks;
./System/Library/Frameworks/CoreData.framework/Headers/NSManagedObjectContext.h:149:#property
(nonatomic, readonly, strong) NSMutableDictionary *userInfo
NS_AVAILABLE(10_7, 5_0);
./System/Library/Frameworks/Foundation.framework/Headers/NSAttributedString.h:54:#property
(readonly, retain) NSMutableString *mutableString;
./System/Library/Frameworks/Foundation.framework/Headers/NSExpression.h:127:#property
(readonly, copy) id (^expressionBlock)(id __nullable, NSArray *,
NSMutableDictionary * __nullable) NS_AVAILABLE(10_6, 4_0);
./System/Library/Frameworks/Foundation.framework/Headers/NSThread.h:24:#property
(readonly, retain) NSMutableDictionary *threadDictionary;
./System/Library/Frameworks/GameplayKit.framework/Headers/GKRuleSystem.h:54:#property
(nonatomic, retain, readonly) NSMutableDictionary *state;
./System/Library/Frameworks/ModelIO.framework/Headers/MDLMesh.h:137:#property
(nonatomic, readonly, retain) NSMutableArray *submeshes;
Only seven results, and the one in NSExpression doesn't count because the "Mutable" that the search found is an argument to the Block that is actually the property's value.
For the others, I think you'll find that the appropriate class reference doc tells you what you can and can't do with the values.
For example, the documentation for threadDictionary has this to say:
You can use the returned dictionary to store thread-specific data.[...]You may define your own keys for the dictionary.
A mutable dictionary is returned precisely so that you can mutate it. The thread object doesn't let you set it, however, so that it can also store things there.
The hit in NSAttributedString.h is actually in the NSMutableAttributedString class, and those docs note:
The receiver tracks changes to this string and keeps its attribute mappings up to date.
Since NSAttributedString is pretty explicitly* just an NSString packaged up with a bunch of attributes, the design of the class exposes the wrapped string directly; the mutable version follows suit.
UIButton was mentioned in the comments, because there you have a read-only label whose own properties are modifiable. And there again, the docs are explicit:
Although this property is read-only, its own properties are read/write. Use these properties primarily to configure the text of the button.
and
Do not use the label object to set the text color or the shadow color.
In summary, there's no way in Objective-C at the language level to create or enforce mutability restrictions. As you've noted, a property marked readonly simply means there's no way for you to set the value to something else.** And there's no equivalent of const_casting the value to be mutable so that you can change it: you will end up with a new value that the vendor object knows nothing about.
The Cocoa convention, then, is to secondarily enforce the property's status by using immutable classes. (In some cases you might even be getting an immutable copy of data that the class internally retains as mutable.) If the API gives you a mutable object, you can assume that you may mutate it, but the documentation should tell you exactly how you can use it.
*Its class description says: "An NSAttributedString object manages character strings and associated sets of attributes (for example, font and kerning) that apply to individual characters or ranges of characters in the string."
**There is KVC, but that's again at the framework level, and framework convention would indicate that you're asking for trouble doing that.
In iOS 8 NSManagedObject was given a new property:
#property (nonatomic, getter=isDeleted, readonly) BOOL deleted;
Unfortunately, I have already been using a property of the same name in my subclasses of NSManagedObject to mark objects as soft deleted.
#property (nonatomic, retain) NSNumber *deleted; // BOOL in CoreData model
This results in a ton of compiler warnings:
'getter' attribute on property 'deleted' does not match the property inherited from 'NSManagedObject'
How does Objective-C handle a superclass and subclass having the same property? I obviously plan on renaming this property across the board but there is already multiple versions of this app in the wild and I am curious of the implications.
#property accessors are just regular methods, so they're handled exactly the same way as other methods. In this case, I believe you luck out because the framework (NSManagedObject) getter is -isDeleted rather than -deleted. Since your subclass implements -deleted, there's technically no conflict with the getter.
Now, the real issue will be, if NSManagedObject internally implements the corresponding -setDeleted: setter, your subclasses implementation of that method will be called instead of NSManagedObject's implementation. A quick check using -instancesRespondToSelector: seems to reveal that NSManagedObject does not actually implement -setDeleted: (on OS X, anyway). So, you might be OK.
The actual implications of a conflict like this for your app (assuming a case where you weren't lucky as described above) are not clear to me, not knowing exactly what your app is doing or why. For what it's worth, the -isDeleted method on NSManagedObject is not at all new. It was simply promoted to a declared #property in iOS 8 (presumably to ease Swift interoperability). It was probably always a bad idea to have your own deleted property...
The title pretty much explains it all.
I simply have an NSDictionary initialised with a bunch of keys and values in one method, but I would like another method to be able to access it within the same class. Is this possible?
Uhm, ever heard of such amazing and totally new and never before seen concepts such as instance variables and properties?
#property (nonatomic, copy) NSDictionary* mySharedDictionary;
Define your NSDictionary as a property (mine called 'dictionary')either in your .h or .m file. Initialise it anywhere in your code and call it with self.dictionary, goodluck!
Suppose I have a Contact object with a name and photo only
#interface Contact: NSObject
{
NSString *name;
UIImage *photo;
}
#property (copy) NSString name;
#property (copy) UIImage photo;
suppose further that I live in a place where I have 1000 people called Sam and they're all clones so their photo look exactly the same and I decided to cheat and use the same photo for all of them.
Is Objective C smart enough that when a copy of the name and photo is taken it would recognise it has the same value and can be handled by one object and allocate just one NSString and one UIImage for all the Sams?
What is the correct way to handle this? Clearly I do need to make the them copy properties to handle non Sam cases and in case I get passed mutable strings and images.
UIImage doesn't conform to the NSCopying protocol, so the question is fairly moot. You need to change that copy on the photo property to a retain. This is perfectly fine for you since UIImage isn't mutable anyway (and has no mutable subclasses), so there's no need to copy it.
As for the name, calling -copy on immutable objects (that support copying) tends to just retain the object instead of copying it. Calling it on the mutable variant of these objects (e.g. NSMutableString) will produce an immutable copy (which will itself respond to subsequent -copy requests by retaining). Note, however, that calling -copy on a mutable object multiple times will produce distinct immutable copies.
If you use [UIImage imageNamed:] then the sdk will cache the photo for you and reuse it. Also, since UIImage is immutable you can't really change it after it has been loaded into memory.
Image objects are immutable, so you cannot change their properties
after creation. This means that you generally specify an image’s
properties at initialization time or rely on the image’s metadata to
provide the property value.
If you want to make your Contact conform to NSCopying, just make sure you use copy on the NSString when duplicating your objects.
I'm a bit confused; if an object is declared in the .h file it is considered automatically as "public" right? We use a #property in the .h file, however, to edit them? This is where I don't understand: we use the getter/setter for private objects, so why do we use the #property for objects declared in the .h file and thus considered as "public"?
Second thing, I found this example: I don't understand why we use a #synthesize for primaryKey in this code: http://staging.icodeblog.com/wp-content/uploads/2008/08/9-todom1.png
and why we don't use a #property for the database object?
It is not correct that if an object (ivar) is declared in a .h file, then it is public. It is only if getter/setter methods are provided, otherwise it is not.
Indeed, the #property/#synthesize directives are facilities meant to declare and define default getter/setter methods. So, instead of writing them yourself, you just use the directives.
It is also worth noting that declaring properties you get the possibility of using the dot notation to refer properties of your objects. And also that they clarify a lot, thanks to the retain/assign/copy specifiers, how memory is meant to be managed for that properties. (And, of course, #synthesize will just do that correctly for you).
About your sample, in fact, whether an ivar is associated to a property or not is a design choice. Possibly, you just reconsider the assumption that ivars declared in .h files are public by defaults, and it will become clearer. In other words: primaryKey is public, database is not.
A very nice tutorial can be found here but also do not forget Apple docs.
EDIT:
about your question from the comment section:
it is not necessary that every ivar has a property, nor that it has getter/setter in order to be used inside of that class implementation.
#interface SomeClass : NSObject {
AnotherClass* _anotherClassObj;
AThirdClass* _aThirdClassObj;
}
#property (nonatomic, retain) AnotherClass* anotherClassObj;
#end
So, here you have two ivars; only one has got a #property declaration. In your .m file you may have, e.g.
#implementation SomeClass;
#synthesize anotherClassObj = _anotherClassObj;
- (void)initWithClasses:(AnotherClass*)obj1 and:(AThirdClass*)obj2 {
.....
self.anotherClassObj = obj1;
_aThirdClassObj = obj2;
...
}
....
#end
In this code:
#synthesize will provide implementation for getter/setter for anotherClassObj so you can use syntax: self.anotherClassObj = obj1; that syntax can be used equally from inside and outside the class implementation;
when you have no getter/setter (either auto-generated or custom) you can assign directly to an ivar by using the syntax _aThirdClassObj = obj2;, with the semantics of simple pointer copy; anyway, _aThirdClassObj will not accessible from outside that class;
furthermore, #property ... anotherClassObj notwithstanding, you can still refer _anotherClassObj directly in your .m file, like in _anotherClassObj = xxx, bypassing getter/setter, if you ever need it.
One thing you should have clear is that getter/setter are not only a way to make an ivar "public". They also play an important role in managing the retain count (depending on which specifier you choose among retain/assign/copy in the property declaration). So, in self.anotherClassObj = obj1; above, obj1 is assigned to _anotherClassObj and it is also retained (and if _anotherClassObj was previously pointing to an object, that object will be sent a release). Raw ivar assignment does not provide that kind of facility.
In my opinion, the retain count management feature of properties is far more important than visibility for deciding whether I use a property or not.
Not everything in the header is public, by default ivars (items in the { }) are #protected. The purpose of the #property is data encapsulation. #synthesize or #dynamic is used for declaring the way you want to implement your property and one or the other is necessary to prevent crashes and warnings.
Resources:
Defining Classes #protected, #package, #private, #public reference
Declared Properties #property reference