Can/How to Replace My Kvo Stuff with Rc3

Why is Objective-C ARC deallocation dependent on whether an object was created in function?

ARC enforces the same memory management semantics you're supposed to follow when you manually retain and release your objects. So let's look at how we would write this under MRR and see if that tells us anything:

GRMemoryChecker *itemMakerFunc()
{
return [[[GRMemoryChecker alloc] initWithName:@"func-based checker" ] autorelease];
}

int main(int argc, const char * argv[])
{
@autoreleasepool {
GRMemoryChecker *checkerLocallyCreated = [[GRMemoryChecker alloc] initWithName:@"locally-created checker"];
GRMemoryChecker *checkerFuncBased = itemMakerFunc();

NSLog(@"Before setting func-based checker pointer to nil");
checkerFuncBased = nil;
NSLog(@"After setting func-based checker pointer to nil");

NSLog(@"Before setting locally-created checker pointer to nil");
[checkerLocallyCreated release];
checkerLocallyCreated = nil;
NSLog(@"After setting locally-created checker pointer to nil");
}
return 0;
}

So basically, the reference returned by alloc is an owning reference, which needs to be released. When an object only lives in one function, we can release it directly and it will be immediately deallocated. But when we're returning it from the function, we can't simply release it, because it needs to live past the return statement, so it gets autoreleased.

Difference between .create() and .createWithMixins() in ember

From Wikipedia:

In object-oriented programming languages, a mixin is a class, which
contains a combination of methods from other classes. How such
combination is done depends on language, but it is not by inheritance.
If a combination contains all methods of combined classes it is
equivalent to multiple inheritance.

In Ember instances of objects are created with the create method with no arguments, or with a single hash(kvo) that represent the properties of that type, and they will be automatically populated. Example:

var SomeClass = Ember.Object.extend({
name: '',
url: ''
});

// this instance will have a "name" and a "url" properties with blank values
var someInstance = SomeClass.create();

// this instance will have the same properties, but now
// their values will be populated
var anotherInstance = SomeClass.create({
name: 'Ember.js',
url: 'http://emberjs.com'
})

On the other hand, crateWithMixins, allow you to mix another class definition into a single object instance or into another class. So let's say you have the same SomeClass above, but you don't want to sub-class it via extend and create another type. In this case you can use a Mixin to make sure that only that one instance will have that definition of the two classes. Example:

var SomeClass = Ember.Object.extend({
name: '',
url: ''
});

// note that you don't extend a mixin, you only create
var SomeOtherClass = Ember.Mixin.create({
doSomething: function() {
console.log('doing my thing');
}
});

// This instance will have a method called "doSomething"
var x = SomeClass.createWithMixins(SomeOtherClass, {
name: 'Ember.js',
url: 'http://emberjs.com'
});

// this instance only has methods/properties defined in "SomeClass"
// therefore, no method called "doSomething"
var y = SomeClass.create({
name: 'Google',
url: 'http://google.ca'
});

However, if you want to create a new class with a Mixin, you can extend Em.Object, passing the Mixin as the first argument, as follows:

var AnotherClass = Ember.Object.extend(SomeOtherClass, {
firstName: '',
lastName: ''
});

var z = AnotherClass.create();
z.set('firstName', 'Hiro');
z.set('lastName', 'Nakamura');
z.doSomething();

Check out the API Documentation as well as this JSFiddle.

Edit: As for _super(), you only use this when you create a new class (via extend). When you create instances of existing classes, you shouldn't call _super().

Another thing. I see you're trying to create a View directly. I believe, based on your code, you should be extending Ember.View and let the framework create instance for your at the appropriate time. If you create manually, you'll be responsible for some parts of its workflow like appending it to the DOM, removing it, etc. Maybe I don't see the whole picture, but based on this code alone, I think you should not call create there and call extend instead, and then you'll be able to call _super()

FinderSync Extension - requestBadgeIdentifierForURL is never called

I already commented on your question but figured I should post a more complete answer.

It sounds like the issue you're having is another Finder Sync extension is "greedily" observing all folders, most likely the Dropbox Finder Integration. Try disabling all other Finder Sync extensions (under System Preferences -> Extensions -> Finder) and re-run your test.

If this resolves the issue, the problem is that Dropbox (or another app) has already called beginObservingDirectoryAtURL for the folder you're trying to monitor. Unfortunately, Apple's API is lacking in that there is no intelligent logic to who gets to monitor a folder when there are conflicting extensions. Currently, whichever Finder Sync extension starts first will "win".

Dropbox greedily monitors all folders under the user's home directory. I've written to both Apple and Dropbox to address this, but haven't heard any response. Currently, the (ugly) workaround I've implemented is to shutdown known "greedy" extensions, start my own extension, then restart the greedy extension.

Custom UIButton Memory Management in dealloc

This is more than a little bit tricky. I have summarized my answer in 5 parts:

  1. Creating a custom init method that returns a different object
  2. WARNING: beware of illegal memory access!
  3. How to properly transfer ownership of the button to its parent view
  4. Specific answers to specific questions
  5. A suggestion for improvement

Part 1 : Creating a custom init method that returns a different object:

This is an example of a very special case, namely that the object returned from -initWithTitle:frame: is not the same "object" that was sent the message in the first place.

Normally speaking, the process goes like this:

instance = [Class alloc];
[instance init];
...
[instance release];

Of course, the alloc and init calls are usually grouped together into one line of code. The key thing to notice here is that the "object" (nothing more than an allocated block of memory at this point) which receives the call to init has already been allocated. If you return a different object (as in your example), you are responsible for releasing that original block of memory.

The next step would be to return a new object that has the proper retain count. Since you are using a factory method (+buttonWithType:), the resulting object has been autoreleased, and you must retain it to set the proper retain count.

Edit: A proper -init method should explicitly test to make sure that it is working with a properly initialized object before it does anything else with that object. This test was missing from my answer, but present in bbum's answer.

Here is how your init method should look:

- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
if (self == nil) { return nil; }
[self retain]; // set the proper retain count
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}

Part 2: WARNING: beware of illegal memory access!

If you are allocating an instance of CustomButton, and then replacing it with an instance of UIButton, you could easily cause some very subtle memory errors. Let's say CustomButton has some ivars:

@class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
@end;

Now, when you replace the allocated CustomButton with an instance of UIButton in your custom init... method, you are returning a block of memory that is too small to hold a CustomButton, but your code will continue to treat this block of code as if it is a full-sized CustomButton. Uh oh.

For example, the following code is now very, very bad:

- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self retain]; // set the proper retain count

someOtherVar = 10; // danger, Will Robinson!

return self;
}

Part 3: How to properly transfer ownership of the button to its parent view:

As for your view controller's dealloc method, you will have to call [myButton release] if you have initialized the button as shown. This is to follow the rule that you must release anything that you alloc, retain or copy. A better way to deal with this issue is to let the controller's view take ownership of that button (which it does automatically when you add the button as a subview):

myButton = [[CustomButton alloc] initWithTitle:@"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1

Now, you never have to worry about releasing that button again. The view owns it, and will release it when the view itself is deallocated.


Part 4: Specific answers to specific questions:

Q: The way I understand it, myButton is not actually retained, even though I invoked it using alloc, because in my subclass I created an autorelease button (using buttonWithType:).

Correct.

Q: In dealloc, does this mean that, when dealloc is called the superview releases the button and its retain count goes down to 1? The button has not yet been autoreleased?

Also correct.

Q: Or do I need to get that retain count down to zero after calling [super dealloc]?

Sort of :) The retain count may or may not drop down to zero at the point when you log it. Autoreleased objects may still have a retain count of one, since they effectively belong to the autorelease pool for the current run loop. For that matter, the view itself may still belong to a window which has not yet been released. The only thing you really need to worry about is balancing out your own memory management. See Apple's memory management guide for details. From the point of view of your viewController, you have allocated the button once, so you must release it exactly once. When it comes to your custom init... method, things get a little bit trickier, but the principle is the same. A block of memory has been allocated, so it must be released (part 1), and, (part 2) init should return an object with a retain count of one (to be properly released later on).


Part 5: A suggestion for improvement:

You could avoid most of the custom initializer mess by simply creating your own factory method in the same spirit as the one provided by UIButton:

+ (id)buttonWithTitle:(NSString *)title frame:(CGRect)btnFrame {
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
button.frame = btnFrame;
return button;
}

Note that this approach can still result in memory access errors as identified in part 2



Related Topics



Leave a reply



Submit