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.
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
I have an existing Parse database, with several PFObject subclasses.
If I want to add a new property to a subclass, an array for example, I add it to the PFObject subclass as an #property and declare it #dynamic in the implementation. I initialise it as empty for new objects.
#dynamic newArray;
+ (instancetype)object
{
MyObject *myObject = [super object];
myObject.newArray = [NSMutableArray array];
return myObject;
}
But how can I ensure this is initialised for already existing objects, as accessing this property for old objects causes a crash.
I figured I could override the getter, but I would lose all the Parse boilerplate code, and not sure that's wise. Currently I have to empty my db to ensure all new objects have the right properties. But this obviously won't work in the real world.
This is a problem that I faced when developing my Parse application as well, but I did not have the problem of preexisting instances of the object that did not have a value for the specified key.
One solution to this problem would be to set up a cloud code job that runs through all the preexisting objects and assigns default values for the required fields. This would enable you to keep the complexity of the client to a minimum, allowing for easy updates and possibly porting to other systems.
You should also set up a beforeSave function that makes sure that all fields have a default value, and then either rejecting ones that don't or silently assigning them a default value.
My third and final recommendation (which you seem to be doing already) is to make sure the +object method properly initializes the object with all default values, in order to ensure that pinned objects (if you're using LDS) have the correct default value.
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 have class Item and class List (which has an NSMutableArray).
Every time class Item is instantiated (and destroyed) it posts a notification, which is listened-to by class List. When class List receives the notification is adds the instance of class Item to its list.
I'm trying to have class Item also post a notification that its about to be dealloc'd. The problem is that class List's NSMutableArray retains the instance of class Item.
What's the most appropriate means of handling this situation? If I decrement the count when adding it to List's array, then an exception will be thrown when class List attempts to call removeObject (since it'll try to dealloc the object.)
Basically, I want a "monitor" class List that contains a list of all "live" instances of Item. But, I also need the ability to release/dealloc the instances and have them report they're being dealloc'd so List can remove them from its NSMutableArray.
Thanks for your help.
If I understand correctly, you want an array that maintains weak references to its items, as opposed to strong references?
I don't know of a way to do this with anything "built-in" in Cocoa. The only way I'd know of to do this is to make the array yourself, and have the storage be __weak id[]. That would automatically zero-out the place in the array when the object deallocates. If you're under the retain-release model, you could use something like MAZeroingWeakRef to get the same behavior.
This is definitely an interesting question, and I don't know of an easier answer. I'd love to be proven wrong!
Ha, I love being wrong!
There's a class called NSPointerArray that looks like it can do what you're looking for. However, it's only available on the Mac, and it only auto-zeros when you're using garbage collection.
I'll keep thinking about this. This is an interesting problem! :)
So I kept thinking about this, and came up with a solution. It uses two unconventional things:
A subclass of NSMutableArray (egads!)
Using an associated object to determine object deallocation
For the first bit, I had to to subclass NSMutableArray so that I could inject some custom logic into addObject: (and related methods). I didn't want to do this via swizzling, since NSArray and friends are a class cluster, and swizzling into/out of clusters is fraught with peril. So, a subclass. This is fine, but we're going to lose some of the awesome features we get from "pure" NSArray instances, like how they do weird things when they get big. Oh well, such is life.
As for the second bit, I needed a way for any arbitrary object to notify that it is about to or just finished deallocating. I thought of dynamically subclassing the object's class, injecting my own dealloc/finalize method, calling super, and then smashing the isa of the object, but that just seemed a little too crazy.
So, I decided to take advantage of a fun little thing called associated objects. These are to ivars what categories are to classes: they allow you to dynamically add and remove pseudo-instance variables at runtime. They also have the awesome side effect of getting automatically cleaned up with the object deallocates. So what I did is just created a little throw away object that posts a notification when it is deallocated, and then attached it to the regular object. That way when the regular object is deallocated, the throw away object will be as well, resulting in a notification being posted, which I then listen for in the NSMutableArray subclass. The notification contains a (stale) pointer to the object that is in the process of getting destroyed, but since I only care about the pointer and not the object, that's OK.
The upshot of all of this is that you can do:
DDAutozeroingArray *array = [DDAutozeroingArray array];
NSObject *o = [[NSObject alloc] init];
[array addObject:o];
NSLog(#"%ld", [array count]); //logs "1"
[o release];
NSLog(#"%ld", [array count]); //logs "0"
The source is on github, and it should (theoretically) work just as well on iOS as Mac OS X (regardless of GC mode): https://github.com/davedelong/Demos
Cheers!
... and I just thought of a way to do this without a custom subclass, but I'm tired and will post the updated answer tomorrow.
the next morning...
I've just updated the project on Github with an NSMutableArray category that allows you to create a true NSMutableArray that auto-zeroes its objects as they're deallocated. The trick was to create a CFMutableArrayRef with a custom retain callback that sets up the proper observation, and then just cast that CFMutableArrayRef to an NSMutableArray and use that (ah, the magic of Toll-Free Bridging).
This means you can now do:
NSMutableArray *array = [NSMutableArray autozeroingArray];
I added a typedef to define these as NSAutozeroingMutableArray, just to make it explicitly clear that while this is an NSMutableArray, it doesn't retain its objects like a normal NSMutableArray. However, since it's just a typedef and not a subclass, you can use them interchangeably.
I haven’t tested this, so comments are welcome.
You could use an NSPointerArray for the list (in a retain property):
self.array = [NSPointerArray pointerArrayWithWeakObjects];
When an Item object is created, it would post a notification that’s listened by your List class. Upon receiving the notification, List adds the object to the pointer array:
[array addPointer:pointerToTheObject];
In this setting, the pointer array doesn’t keep a strong reference to its elements — in particular, it doesn’t retain them. This applies to both garbage-collected and non-garbage-collected builds.
In a garbage-collected build, if an element is garbage collected then the garbage collector automatically assigns NULL to the position in the array where the object was stored.
In a non-garbage-collected build, you’ll need to manually remove the element or assign NULL to the position in the array where it was stored. You can do this by overriding -[Item dealloc] and posting a notification that the object is being deallocated. Your List class, upon receiving the notification, would act upon it.
Note that, since objects are not owned by the pointer array, you must keep a strong reference to (or retain) them if you want to keep them alive.
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?