问题

Xcode 9.1
学习StoryBoard,想自定义一个xib,对一个XXView添加一个xib,取名为XXView.xib,运行的时候发现,竟然奔溃了…粗略查找了下,发现可能跟我有一个XXViewController类(没有带xib)有关。于是便做了一下尝试。

过程

有一个AAViewController控制器(创建时候没有点上),就创建了一个AAView.xib,画好界面后发现push进入这个控制器就会奔溃,报如下信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2017-11-17 16:51:29.581676+0800 CleanTest[28157:4303971] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "AAView" nib but the view outlet was not set.'
*** First throw call stack:
(
0 CoreFoundation 0x000000010a4a41ab __exceptionPreprocess + 171
1 libobjc.A.dylib 0x0000000107329f41 objc_exception_throw + 48
2 CoreFoundation 0x000000010a518cb5 +[NSException raise:format:] + 197
3 UIKit 0x0000000107d62965 -[UIViewController _loadViewFromNibNamed:bundle:] + 684
4 UIKit 0x0000000107d63164 -[UIViewController loadView] + 177
5 UIKit 0x0000000107d63495 -[UIViewController loadViewIfRequired] + 195
6 UIKit 0x0000000107d63cf2 -[UIViewController view] + 27
7 UIKit 0x0000000107d95d4e -[UINavigationController _startCustomTransition:] + 954
8 UIKit 0x0000000107dac5a4 -[UINavigationController _startDeferredTransitionIfNeeded:] + 686
9 UIKit 0x0000000107dad8c3 -[UINavigationController __viewWillLayoutSubviews] + 150
10 UIKit 0x0000000108006a49 -[UILayoutContainerView layoutSubviews] + 231
11 UIKit 0x0000000107c8f6f5 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1439
12 QuartzCore 0x000000010d9a33ee -[CALayer layoutSublayers] + 153
13 QuartzCore 0x000000010d9a74dd _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 401
14 QuartzCore 0x000000010d92fded _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 365
15 QuartzCore 0x000000010d95b704 _ZN2CA11Transaction6commitEv + 500
16 QuartzCore 0x000000010d95c450 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76
17 CoreFoundation 0x000000010a446d37 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
18 CoreFoundation 0x000000010a446c8e __CFRunLoopDoObservers + 430
19 CoreFoundation 0x000000010a42b254 __CFRunLoopRun + 1572
20 CoreFoundation 0x000000010a42a9b9 CFRunLoopRunSpecific + 409
21 GraphicsServices 0x000000010cb569c6 GSEventRunModal + 62
22 UIKit 0x0000000107bbe5e8 UIApplicationMain + 159
23 CleanTest 0x000000010686c28f main + 111
24 libdyld.dylib 0x000000010b515d81 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

渣渣的我自然看不懂,然后就做了尝试:

试验

  1. 把AAView.xib -> File’s Owner -> Class设置为AAViewController,运行:
    同样报错奔溃

  2. 把AAView.xib -> File’s Owner -> Class设置为AAViewController,control按住拖线从 File’s Owner 到 View,运行:
    成功push到AAViewController,显示的是AAView.xib

  3. 新建一个BBViewController,在新建的时候勾选上Also create XIB file,push到BBViewController,运行:
    成功push到BBViewController,显示的是BBViewController.xib

  4. 新建一个BBViewController,在新建的时候勾选上Also create XIB file,然后在新建一个BBView.xib,push到BBViewController,运行:
    成功push到BBViewController,显示的是BBViewController.xib

对比1和2,3和4,很明显,xib和viewController差了个连接关系,所以我打开BBView.xib和BBViewController.xib的源文件显示,对比了下:

BBView.xib:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
</view>
</objects>
</document>

BBViewController.xib:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="BBViewController">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
</view>
</objects>
</document>

主要差别就在于第一个里面有没有:

1
2
3
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>

其实就是试验2拖线的操作,就是差一个viewController和xib的连接关系。
所以我猜想,当你创建XXXViewController的时候如果没有勾选上Also create XIB file,那么系统在加载这个ViewController的时候在有XXXView.xib的情况下会自动加载这个xib,如果这个xib并没有连接到XXXViewController时,便会抛出异常,奔溃闪退。
如果勾选上了,加载的便是系统创建的XXXViewController.xib,无论是否有XXXView.xib都不影响正常运行。

解决

所以要我们在有XXXViewController没自带XXXViewController.xib的时候,不能为其他View或者ViewController创建连接XXXView命名的xib,因为这已经被XXXViewController占有了,必须用其他命名或者给XXXViewController指定连接另一个xib。