【问题标题】:objective c class method returned value, assigned to weak/strong properties客观 c 类方法返回值,分配给弱/强属性
【发布时间】:2012-12-05 18:06:23
【问题描述】:

我在涉及弱属性和强属性时遇到了一些困惑。为简洁起见,我不会包含整个代码。

我创建了一个返回 UIView 对象的类便捷方法,并在 UIView 类别中实现它作为子类化的替代方法。

@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;
}

我应该注意,在方法内部我没有任何指向monthView的东西。

现在在“所有者”的实现中,这是一个名为 CSCalendarViewController 的类,我通过调用类便利方法创建上述 UIView 并将其分配给名为 _monthView 的 UIView 属性。

@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];
        }

现在我的困惑是这样的。即使我将属性“monthView”定义为弱,“monthView”仍然保留返回的 UIView 的值。

如果我继续做这样的事情:

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

编译器发出警告(应该如此)说“将保留对象分配给弱变量”。

当我将“monthView”分配给从类方法返回的 UIView 时,为什么没有收到相同的错误消息?

我对 pre-ARC 内存管理没有深入的了解,而且我认为我遗漏了一些明显的东西。谢谢。

【问题讨论】:

    标签: iphone objective-c cocoa-touch ios6 xcode4.5


    【解决方案1】:

    'monthView' 仍然保留返回的 UIView 的值。

    不会太久。这个问题展示了 ARC 的基本工作原理,以及它如何转换为传统的保留/释放方法,而不是一个全新的内存管理系统。

    弧前

    在 ARC 之前,没有弱或强的概念,而是指保留和分配。分配给变量对引用计数没有任何作用,由开发人员来管理它。

    现在,关于内存管理策略,名称以“alloc”、“new”、“copy”或“mutableCopy”开头的方法将返回一个保留对象 (Documentation)。这意味着,在分配给变量时,开发人员不需要显式保留(他们必须显式释放或自动释放):

    // 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];
    

    没有该命名约定的方法建议它们返回一个自动释放的对象。开发人员需要明确说明,以使对象保持更长的时间:

    // 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 消除了这种不必要的释放和保留需求,而是根据分配给它的变量类型来决定如何处理内存管理。这并不意味着内存管理模型发生了变化。它仍然是引擎盖下的所有保留和释放。因此,这对您有何影响?为简洁起见,此答案仅考虑弱变量。

    分配给弱变量不会对对象的保留计数做任何事情。让我们看一个实际的例子来解释:

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

    因为(实际上,在 ARC 的背后)这是返回一个保留对象,但弱变量不会影响保留计数,所以编译器已经找到了释放对象的最早点,以防止内存泄漏;关于分配!因此,它将被翻译成以下内容,并导致错误:

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

    现在,关于monthViewFromDateArray:,这是向编译器建议(由于其名称),它将返回一个自动释放的对象(Documentation)。因为编译器知道自动释放的对象将在稍后的运行循环中自动释放(当自动释放池耗尽时),它不会像以前那样插入release 调用。因此,对弱变量的赋值不是问题,但它只在它所使用的范围内才真正有效。

    【讨论】:

    • 我测试并确认了您的说法,即monthView 不会长时间指向 UIView 对象。我在 viewDidAppear 中放置了一个 NSLog,果然它显示为零。在离开 viewWillAppear 范围之前,MonthView 具有非零值。如果可以的话 +1。
    【解决方案2】:

    说我们有方法

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

    编译的时候转成这个

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

    现在在这里:

    UIView* __weak view;
    
    //warning here
    view = [[UIView alloc] init]; //1
    
    view = [AppDelegate create];  //2
    

    第一行转换成这样:

    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)
    

    第二行:

    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
    

    如果我们有这样的代码:

    @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
    

    您可以通过查看代码反汇编来了解所有这些。

    【讨论】:

    • 这消除了编译器在后台所做的事情的魔力,并解释了为什么我认为我看到的结果不一致。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2014-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    • 2019-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多