【问题标题】:Interface Builder encoding界面生成器编码
【发布时间】:2014-01-10 12:58:26
【问题描述】:

根据Apple's documentation,Interface Builder 实例化对象,然后在设计时将其序列化并打包到 NIB/XIB 文件中。在运行时,整个对象图通过 initWithCoder: 反序列化。

这是文档:

在 Interface Builder 中创建的视图实例在加载其 nib 文件时不会调用 initWithFrame:,这通常会导致混淆。请记住,Interface Builder 在保存 nib 文件时会归档对象,因此视图实例已经被创建并且 initWithFrame: 已经被调用。

由于对象是通过 initWithCoder: 实例化的,我认为对象图是通过其对应的 encodeWithCoder: 序列化的。但是,请考虑以下代码:

@interface CustomView : UIView
@end

@implementation CustomView

-(id)initWithCoder:(NSCoder *)aDecoder{
    if(self = [super initWithCoder:aDecoder]){
        NSNumber * n = [aDecoder decodeObjectForKey:@"DummyKey"];
        NSLog(@"%@", n);
    }
    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder{
    [super encodeWithCoder:aCoder];
    [aCoder encodeObject:@YES forKey:@"DummyKey"];
}

@end

如果我将此 CustomView 放在 Interface Builder 的情节提要上,它会打印 "(null)" 而不是 "YES"。为什么会这样? Interface Builder如何序列化对象图?我是否误解了文档?

【问题讨论】:

  • 请注意,您可以在 IB 中添加“用户定义的运行时属性”(选择视图并转到检查器的第三个选项卡)。这些将在您的对象未归档 IIRC 时设置在您的对象上(使用您的属性的 setter 方法)。

标签: ios objective-c xcode interface-builder xib


【解决方案1】:

encoderWithCoder:方法不会被调用,因为XCode将对象图编码成xib的时间不是运行时间,它不能调用你的encoderWithCoder:方法。

How does Interface Builder serialize the object graph?

如果你打开Xib作为源代码,你可以看到它是这样的:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
    <dependencies>
        <deployment defaultVersion="1536" identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="EMXibViewController">
            <connections>
                <outlet property="view" destination="1" id="3"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="1">
            <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <view contentMode="scaleToFill" id="XyJ-CF-vRx">
                    <rect key="frame" x="68" y="102" width="204" height="403"/>
                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
                </view>
            </subviews>
            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
            <simulatedStatusBarMetrics key="simulatedStatusBarMetrics"/>
            <simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina4"/>
        </view>
    </objects>
</document>

我们可以看到xib只保留了object的non-default值以及它们之间的关系。

xib 一个 UIViewController 并且只有两个 UI 对象,一个是 UIViewController 的视图,另一个是子视图让我们说它mysubview 并谈论 XCode 如何归档 mysubview

我修改了mysubview的backgroundColor、frame和autoresizingMask属性,以backgroundColor属性为例,backgroundColor属性保存为"color key="backgroundColor" white="0.0" alpha="1" colorSpace ="校准白""。它将归档mysubview的背景颜色。

Xib 只是归档 mysubview 对象的 backgroundColor 和 autoresizingMask 和 frame 属性,当加载 xib 时,它会调用 UIView 的 initWithCoder 方法。在该方法中,它将获取键@“backgroundColor”的UIColor对象和键@“frame”的CGRect和键@“autoresizingMask”的UIViewAutoresizing,UIView的任何其他属性都分配了默认值。

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder] ;
    UIColor * backgroundColor = [aDecoder decodeIntegerForKey:@"backgroundColor"] ;
    if (backgroundColor) {
        self.backgroundColor = backgroundColor ;
    } else {
        self.backgroundColor = $(defaultColor) ; // if no key in xib, use the default value.
    }
    // more...
    return self ;
}

这是一个很好的问题,让我思考更多。

【讨论】:

  • @TamásZahola 我已经更新了我的答案,可能更清楚了。
【解决方案2】:

Interface Builder 不使用您的代码来生成 xib。就这么简单。 encodeWithCoder 永远不会被 Interface Builder 调用。 Interface Builder 不会运行您的代码。

【讨论】:

  • 那么为什么Apple文档会这样说:“记住Interface Builder在保存nib文件时会归档一个对象,因此视图实例已经创建并且initWithFrame:已经已被调用。” ?它清楚地表明视图是通过 initWithFrame: 实例化的,然后被归档到 NIB 中。
  • @TamásZahola 请注意,Interface Builder 不会创建您自己的类实例。如果您从库中选择 UIView 并将其类更改为 MyView,则不会创建 MyView。对于界面构建器,它仍然只是一个UIView。界面生成器不运行您的代码。
【解决方案3】:

简单来说,如果您在 xib 或 Storybord 中创建对象,那么 initWithFrameinitWithCoder 将无法被您覆盖,它们将被框架调用并创建来自 xib 或 Storybord 的对象属性。

【讨论】:

    猜你喜欢
    • 2017-01-15
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 2014-05-20
    • 2011-03-26
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多