objective c class method returned value, assigned

2020-03-26 05:12发布

I'm facing a bit of a confusion involving weak and strong properties. For the sake of brevity I won't include the entire code.

I created a class convenience method which returns a UIView object, and I implemented it in a UIView category as an alternative to subclassing.

@implementation UIView (CSMonthView)    

+ (UIView *)monthViewFromDateArray:(NSArray *)arrayOfAllShiftsAndEvents withNibOwner:(id)owner selectedDate:(NSDate *)selectedDate withCompletionHandler:(void(^)(CSCalendarButton *selectedButton))block
{   // .. do some stuff
    // Create an instance of UIView
    UIView *monthView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320.0, 200.0)];

    // Create UIButtons and set the passed down 'owner' value, as the target for an
    // action event.

    // Add UIButton as subviews to monthView....

    return monthView;
}

I should note that inside the method I do not have anything pointing to monthView.

Now inside the implementation of the 'owner', which is a class called CSCalendarViewController, I create the above UIView by calling the class convenience method and assign it to a UIView property called _monthView.

@interface CSCalendarViewController : UIViewController 

@property (weak, nonatomic) UIView *monthView;

@end


@implementation CSCalendarViewController


     __weak CSCalendarViewController *capturedSelf = self;
    // Create the current month buttons and populate with values.
    _monthView = [UIView monthViewFromDateArray:_arrayOfAllShiftsAndEvents withNibOwner:self selectedDate:_selectedDate withCompletionHandler:^(CSCalendarButton *selectedButton) {

            capturedSelf.selectedButton = selectedButton;
            [capturedSelf.selectedButton setSelected:YES];
        }

Now my confusion is this. Even though I defined the property 'monthView' as weak, 'monthView' still holds on to the value of the returned UIView.

If I go ahead and do something like this:

    _monthView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 200.0)];

The compiler gives a warning (as it should) saying "Assigned retained object to weak variable".

Why am I not getting the same error message when I assign 'monthView' to the UIView that returns from the class method?

I don't have a deep understanding when it comes to pre-ARC memory management, and I think I'm missing something obvious. Thanks.

2条回答
beautiful°
2楼-- · 2020-03-26 06:01

Say we have method

+(UIView*) create {
    return [[UIView alloc] init];
}

It is converted to this when compiled

+(UIView*) create {
    return [[[UIView alloc] init] autorelease];
}

Now here:

UIView* __weak view;

//warning here
view = [[UIView alloc] init]; //1

view = [AppDelegate create];  //2

The first line is converted to this:

tempVar = [[UIView alloc] init];
storeWeak(tempVar, &view); //view = tempVar and marked as weak
[view release]; //view is nil after this because retain count == 0 (assignment to nil is done in release internally)

The second line:

tempVar = [MyClass create];
[tempVar retainAutoreleasedReturnValue];
storeWeak(tempVar, &view); //view = tempVar and marked as weak
[release tempVar]; //view is not nil because tempVar is autoreleased later

If we have the code like this:

@autoreleasepool {
    view = [[UIView alloc] init];
    //view is nil here

    view = [AppDelegate create];
    //view equals to the return value
}
//view becomes nil here because [AppDelegate create] return value is released

You can see all of this by looking at code disassembly.

查看更多
Melony?
3楼-- · 2020-03-26 06:03

'monthView' still holds on to the value of the returned UIView.

It won't for long. This question demonstrates the underlying workings of ARC, and how it is a translation to the traditional retain/release methods, rather than a whole new memory management system.

Pre ARC

Before ARC, there was no concept of weak or strong, instead it referred to retain and assign. Assigning to variables did nothing to the reference count, it was up to the developer to manage it.

Now, in regards to the memory management policies, a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, will return a retained object (Documentation). This meant, on assignment to a variable, the developer didn't need to explicitly retain (they had to explicitly release, or autorelease):

// Will have a retain count of 1 here 
var = [NSString alloc] initWithString:@"Test"];

// Will have a retain count of 2 here 
var = [[NSString alloc] initWithString:@"Test"] retain]

// Will have a retain count of 1 here, but will be released later on automatically
var = [[NSString alloc] initWithString:@"Test"] autorelease];

// Will have a retain count of 0 here, and will be released before it reaches the variable!
var = [[NSString alloc] initWithString:@"Test"] release];

Methods that don't have that naming convention, suggest they return an autoreleased object. The developer needs to say something explicitly, to keep the object around longer:

// Will have a retain count of 1 here, but will be released later on automatically
var = [NSString stringWithString:@"Test"];

// Will have a retain count of 1 here 
var = [[NSString alloc] initWithString:@"Test"] retain]

// Will have a retain count of 1 here, but will be released twice later on (Over-released!)
var = [[NSString alloc] initWithString:@"Test"] autorelease];

// Will have a retain count of 0 here, and will be released again later on (Over-released!)
var = [[NSString stringWithString:@"Test"] release];

ARC + MRC

ARC removes this unnecessary need of releasing and retaining, and instead decides what to do with the memory management based on the type of variable it will be assigned to. This doesn't mean the memory management model changed; it is still all retain and release under the hood. As such, how does this affect you? For brevity, this answer will only take into account weak variables.

Assigning to a weak variable does not do anything with the retain count of the object. Lets see a practical example to explain:

__weak UIView* monthView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 200.0)];

Because (in reality, behind the ARCness) this is returning a retained object, but weak variables don't affect the retain count, the compiler has found the earliest point to release the object in order to prevent a memory leak; on allocation! As such, it will be translated to the following, and cause an error:

UIView* monthView = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 200.0)] release];

Now, in regards to monthViewFromDateArray:, this is suggesting to the compiler (due to its name), that it will return an autoreleased object (Documentation). Because the compiler is aware that an autoreleased object will be released automatically later on in the run loop (when the autorelease pool is drained), it will not insert a release call like before. As such, the assignment to a weak variable isn't an issue, but it's only really valid within the scope it's being used in.

查看更多
登录 后发表回答