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.
Related
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
When creating a class, a BOOL property declaration often looks like this:
#property (nonatomic, getter=isActive) BOOL active;
For normal classes and subclasses, naming the getter is<#Key#> makes sense, looks sharp, and is nice to use in if statements.
What about when naming properties in a category though?
I was just implementing some helper methods on NSView, when all of the sudden, things took a hideous turn:
#property (nonatomic, readonly, getter=bsd_isFieldEditorDelegate) BOOL bsd_fieldEditorDelegate;
After adding the recommended prefix (bsd_ in my case), the semantic value of the getter variation seems to get lost. In this case, should I just remove the getter name altogether or what?
I looked throughout the documentation, but I couldn't find a definitive answer. Is there one, or am I just overthinking it?
You CANNOT use property in Category of Objective-C. Instead of, you should use Associated object of Objective-C Runtime. Because of that, attribute of property is may not work with Associated object.
Reference: Objective-C: Property / instance variable in category
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...
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 kinda new in Obj-C and I would like to ask why everything need to write something like this?
#property (_something, _something) NSString* name;
What is the #property indicates for?
Why we need to put _something in the bracket?
PS: I know there is no _something, it should be nonatomic, retain, copy and so on. Since it has so many options, that's why I simply put a word to indicate the content inside the bracket.
(Where can I get all the list of available options?)
Thanks :D
#property indicates that you are defining a property, which is at the basic level just syntactic sugar that allows you to do variable = object.property and object.property = value instead of variable = [object property] and [object setProperty:value]. If you really wanted, you could skip declaring any properties and just declare the getter and setter methods directly and the runtime would hardly notice the difference.
The things inside the parentheses modify the property. Many are only useful for properties whose getter and/or setter implementations are created for you using #synthesize in the #implementation block. For example, assign (the default) says that the value set is just set; retain says that the object set will automatically have its retain method called (and the previous object, if any, will have release called); and copy says that the object will have its copy called.
A list of possible attributes is in the documentation.
The #property directive is simply a promise that the class provides accessors for the given identifier. Options given after the directive (copy, nonatomic, etc.) specify how the accessors behave. That's really all there is to it.
Related syntax includes the directives #dynamic and #synthesize and dot notation. The two directives are mutually exclusive and tell the compiler that you'll supply the accessors or that the compiler should supply the accessors, respectively. Dot notation is the syntactic sugar that Anomie mentioned -- it's meant for accessing properties, but you're not required to formally declare a property to use it.