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.
Related
I have a NSMutaleArray that contains instances of class X. The tableView cell at row i is populated using contents of array at index i.
The contents of the array are created based on incoming JSON data from the network. So, I have a helper function that converts JSON data into these objects and stores them in the array. The object may be modified after storing in the array (for instance image belonging to cell gets downloaded and file pointer is added to the object).
Currently, I am making the reads and writes thread safe putting them inside dispatch_sync & dispatch_barrier_async. Some issues I see
To create the tableview cell I need to do a read. This is also going through the dispatch_sync. Hence, it looks like there is chance of it getting blocked because data for a non-visible but close enough cell is being updated. Any best practices to avoid this?
Is there some smart way for the block submitted through dispatch_sync and disaptch_barrier_sync to only wait if another block is either modifying, removing the same index of the NSMUtableArray or value associated withs are key of NSMUtableDictionary?
To make the code more readable, I am thinking of sub-classing NSMutableArray and creating a THreadSafeMutableArray class that over-rides the objectForIndex and subscript methods. Any issues with this approach?
Your NSMutableArray should be an atomic property (the default value is this) maybe you changed to nonatomic in the declaration.
Look the documentation: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html
Apple documentation:
This means that the synthesized accessors ensure that a value is always fully retrieved by the getter method or fully set via the setter method, even if the accessors are called simultaneously from different threads.
#interface XYZObject : NSObject
#property NSObject *implicitAtomicObject; // atomic by default
#property (atomic) NSObject *explicitAtomicObject; // explicitly marked atomic
#end
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.
I have a problem with creating my own subclass of NSData, which I want to have a custom description method. Even creating a dummy NSData subclass:
#interface MyData : NSData {}
#end
and
#implementation MyData
#end
and using it results in weird bugs (the function that uses it never exits, and control somehow returns to the run loop). I thought that maybe I am responsible for rewriting the designated initializers of NSData (calling the super implementation), but none is mentioned in the doc. So:
what are the designated initializers of NSData?
what is the bare minimum I need to write for a dummy subclass of NSData?
Making an NSData subclass is difficult because (as drewag noted) it is a member of a class cluster. From the Binary Data Programming Guide:
...data objects are not actual instances of the NSData or NSMutableData classes but instead are instances of one of their private subclasses.
When you do [[NSData alloc] initWith...] you don't get back an NSData; you probably get back an NSConcreteData. The extraordinary Cocoa With Love has a discussion and demonstration of subclassing class clusters.
The best (and most idiomatic) option is probably composition: your custom class should simply contain an NSData ivar, and implement a description method that operates on that enclosed object.
While drewag's response is technically correct, this is a dangerous technique to use on Cocoa classes; it will override the description method of every NSData object in the program, whether you create it directly or not.
In the specific case of the description method this may be okay, but for another method more likely to be relied upon by other objects in the framework, it could cause large, hard-to-trace problems. You should only do this if you are sure that there is no other way.
It would be far better to create a category and method with a prefix:
#interface NSData (FX_Description)
- (NSString *)FX_description;
#end
The Apple docs specifically mention this category-override technique and advise against it:
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.
An earlier version of the docs went on to say:
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. [Emphasis mine.]
If all you want is to override a single function "description" consider using a "Category" instead:
#interface NSData (MyData)
-(NSString*)description;
#end
#implimentation NSData (MyData)
-(NSString*)description
{
return #"something";
}
#end
Then, you can use this function on any instance of NSData.
It is very difficult to subclass NSData because it is a "Class Cluster." The public API treats it as one class, but in reality it is a collection of hidden subclasses. You can research overriding a class cluster, but it is almost never needed. Another option is to create your "MyData" class with NSData as a member variable instead of using a subclass.
I have a pretty standard setup where I have an array in my controller that I'm binding to an NSTableView. When I add an object to this array with the UI, I add elements by copying the array to a mutable array, adding the contents, then copying it back. I saw this idiom somewhere, but I'm not really sure where, or whether this is actually a sane thing to do. I t works fine on Snow Leopard, but it crashes really badly on Leopard. GDB tells me it happens right at the marked line, where I copy the new array back.
#interface MyAppDelegate : NSObject {
NSArray * urls;
//other declarations
}
#property (nonatomic, retain) NSArray *urls;
#implementation MyAppDelegate
#synthesize urls;
- (void)addThing:(id)sender {
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
}
I'm pretty sure I must be doing something boneheaded here. Is there a better way to do this? Why am I crashing?
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
What did you create newUrls for if not to set it as the new value of urls?
Besides that, there are a couple of things you're doing wrong:
No model objects. Bindings hates this. Unless your table view exists solely to display the different parts of the URL (scheme, host, path, etc.), each in one column, you're not giving Bindings what it wants.
Pay attention to the fields in the Bindings Inspector. Note that there are two: Controller Key and Model Key Path. They're exactly what they say on their tins: The Controller Key is the key for a property of the controller, which should provide model objects. The Model Key Path is the key path for one or more properties in the model, which usually should provide value objects, such as strings and numbers.
You probably need to create a model class and make the URL a property of that class. I'd guess that you have other objects, perhaps in parallel arrays, that you can move to properties of the model class. Do that, so that you're working with Bindings instead of against it.
Not using array accessors. You're just setting the whole array at once, which is inefficient and may cause display problems (such as selection highlights disappearing). Implement and use array accessors for this array property. Accessorizer can help you here.
I actually have no idea whether this will fix your crash because you haven't told us anything about it. You should edit your question to include any exception messages or other crash-describing output, which you'll find in the Run Log/Debugger Console.
Also, since the type of urls can be mutable, you should set it to copy, not retain.
NSArray * things; - since this can be modified you better represent using NSMutableArray instead NSArray.
When ever you need to add some new element to the list just use 'addObject' and insert element to the list.
Your binding will make sure that UI is updated using KVO and KVC.
It looks like the problem was that I had NSURLs as my object type. Once I changed the object in my array to a custom-made KVC compliant object, I wasn't getting any more crashes.
Maybe NSURL isn't KVC-compliant on 10.5, but it is on 10.6?
I'm getting sublayers of a CALayer with this property accessor:
// #property(copy) NSArray *sublayers
NSArray* layer = mylayer.layer.sublayers;
Since this property uses "copy", every time I simply execute:
mylayer.layer.sublayers
is an entire copy of the sublayers array being made for me? If so that might be bad, because I could have one hundred+ layers, and definitely don't want to create a huge copy of them. I'm just looking to get a count and iterate through the existing layers, just a pointer to the existing layers,
Thank you
When you use the 'copy' declaration and you #synthesize the property, then copy is used when the property is set.
In your example, you are only accessing the value which just gives you a pointer to the sublayers array.
Here's a link to the ADC documentation on this point.
Update
IIRC, the runtime is smart enough to know if the object being set is mutable. If an immutable object is being passed in to the property it is retained and not copied.
In some cases, if you are passing in a mutable object that you want to be able to modify, then you should write your own setter that calls mutableCopy on the object. This is shown in the documentation link that I provided.
I'm not sure I understand your answer Abizern so let me say this:
If you use (copy) on a property you will be making a whole new copy of that object so would be wasting a lot of memory. I'm not sure why they are doing that, they could just use (readonly) instead if they want to protect the values from change. Plus remember most value classes are immutable in Obj-C so they can't change the value anyway.