Minor fix for autocreated object repeated fields and maps.

- If setting/clearing a repeated field/map that was objects, check the class
  before checking the autocreator.
- Just to be paranoid, don’t mutate within copy/mutableCopy for the autocreated
  classes to ensure there is less chance of issues if someone does something
  really crazy threading wise.
- Some more tests for the internal AutocreatedArray/AutocreatedDictionary
  classes to ensure things are working as expected.
- Add Xcode 8.2 to the full_mac_build.sh supported list.
diff --git a/Makefile.am b/Makefile.am
index 4ece521..5ade9d2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -525,6 +525,7 @@
   objectivec/Tests/GPBDictionaryTests+String.m                               \
   objectivec/Tests/GPBDictionaryTests+UInt32.m                               \
   objectivec/Tests/GPBDictionaryTests+UInt64.m                               \
+  objectivec/Tests/GPBDictionaryTests.m                                      \
   objectivec/Tests/GPBDictionaryTests.pddm                                   \
   objectivec/Tests/GPBMessageTests+Merge.m                                   \
   objectivec/Tests/GPBMessageTests+Runtime.m                                 \
diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh
index ef8fb74..ea9fd27 100755
--- a/objectivec/DevTools/full_mac_build.sh
+++ b/objectivec/DevTools/full_mac_build.sh
@@ -265,6 +265,14 @@
           -destination "platform=iOS Simulator,name=iPad Pro (9.7 inch),OS=10.1" # 64bit
       )
       ;;
+    8.2* )
+      XCODEBUILD_TEST_BASE_IOS+=(
+          -destination "platform=iOS Simulator,name=iPhone 4s,OS=8.1" # 32bit
+          -destination "platform=iOS Simulator,name=iPhone 7,OS=10.2" # 64bit
+          -destination "platform=iOS Simulator,name=iPad 2,OS=8.1" # 32bit
+          -destination "platform=iOS Simulator,name=iPad Pro (9.7 inch),OS=10.2" # 64bit
+      )
+      ;;
     * )
       echo "Time to update the simulator targets for Xcode ${XCODE_VERSION}"
       exit 2
diff --git a/objectivec/GPBArray.m b/objectivec/GPBArray.m
index ae57747..f401631 100644
--- a/objectivec/GPBArray.m
+++ b/objectivec/GPBArray.m
@@ -2519,14 +2519,14 @@
 
 - (id)copyWithZone:(NSZone *)zone {
   if (_array == nil) {
-    _array = [[NSMutableArray alloc] init];
+    return [[NSMutableArray allocWithZone:zone] init];
   }
   return [_array copyWithZone:zone];
 }
 
 - (id)mutableCopyWithZone:(NSZone *)zone {
   if (_array == nil) {
-    _array = [[NSMutableArray alloc] init];
+    return [[NSMutableArray allocWithZone:zone] init];
   }
   return [_array mutableCopyWithZone:zone];
 }
diff --git a/objectivec/GPBDictionary.m b/objectivec/GPBDictionary.m
index fd8bd1c..1c67c68 100644
--- a/objectivec/GPBDictionary.m
+++ b/objectivec/GPBDictionary.m
@@ -13579,22 +13579,26 @@
 
 - (id)copyWithZone:(NSZone *)zone {
   if (_dictionary == nil) {
-    _dictionary = [[NSMutableDictionary alloc] init];
+    return [[NSMutableDictionary allocWithZone:zone] init];
   }
   return [_dictionary copyWithZone:zone];
 }
 
 - (id)mutableCopyWithZone:(NSZone *)zone {
   if (_dictionary == nil) {
-    _dictionary = [[NSMutableDictionary alloc] init];
+    return [[NSMutableDictionary allocWithZone:zone] init];
   }
   return [_dictionary mutableCopyWithZone:zone];
 }
 
+// Not really needed, but subscripting is likely common enough it doesn't hurt
+// to ensure it goes directly to the real NSMutableDictionary.
 - (id)objectForKeyedSubscript:(id)key {
   return [_dictionary objectForKeyedSubscript:key];
 }
 
+// Not really needed, but subscripting is likely common enough it doesn't hurt
+// to ensure it goes directly to the real NSMutableDictionary.
 - (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
   if (_dictionary == nil) {
     _dictionary = [[NSMutableDictionary alloc] init];
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index 4a6f061..9660f1e 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -1023,9 +1023,11 @@
       if (arrayOrMap) {
         if (field.fieldType == GPBFieldTypeRepeated) {
           if (GPBFieldDataTypeIsObject(field)) {
-            GPBAutocreatedArray *autoArray = arrayOrMap;
-            if (autoArray->_autocreator == self) {
-              autoArray->_autocreator = nil;
+            if ([arrayOrMap isKindOfClass:[GPBAutocreatedArray class]]) {
+              GPBAutocreatedArray *autoArray = arrayOrMap;
+              if (autoArray->_autocreator == self) {
+                autoArray->_autocreator = nil;
+              }
             }
           } else {
             // Type doesn't matter, it is a GPB*Array.
@@ -1037,9 +1039,11 @@
         } else {
           if ((field.mapKeyDataType == GPBDataTypeString) &&
               GPBFieldDataTypeIsObject(field)) {
-            GPBAutocreatedDictionary *autoDict = arrayOrMap;
-            if (autoDict->_autocreator == self) {
-              autoDict->_autocreator = nil;
+            if ([arrayOrMap isKindOfClass:[GPBAutocreatedDictionary class]]) {
+              GPBAutocreatedDictionary *autoDict = arrayOrMap;
+              if (autoDict->_autocreator == self) {
+                autoDict->_autocreator = nil;
+              }
             }
           } else {
             // Type doesn't matter, it is a GPB*Dictionary.
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index d453859..68aadb7 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -408,9 +408,11 @@
       if (field.fieldType == GPBFieldTypeRepeated) {
         // If the old array was autocreated by us, then clear it.
         if (GPBDataTypeIsObject(fieldType)) {
-          GPBAutocreatedArray *autoArray = oldValue;
-          if (autoArray->_autocreator == self) {
-            autoArray->_autocreator = nil;
+          if ([oldValue isKindOfClass:[GPBAutocreatedArray class]]) {
+            GPBAutocreatedArray *autoArray = oldValue;
+            if (autoArray->_autocreator == self) {
+              autoArray->_autocreator = nil;
+            }
           }
         } else {
           // Type doesn't matter, it is a GPB*Array.
@@ -423,9 +425,11 @@
         // If the old map was autocreated by us, then clear it.
         if ((field.mapKeyDataType == GPBDataTypeString) &&
             GPBDataTypeIsObject(fieldType)) {
-          GPBAutocreatedDictionary *autoDict = oldValue;
-          if (autoDict->_autocreator == self) {
-            autoDict->_autocreator = nil;
+          if ([oldValue isKindOfClass:[GPBAutocreatedDictionary class]]) {
+            GPBAutocreatedDictionary *autoDict = oldValue;
+            if (autoDict->_autocreator == self) {
+              autoDict->_autocreator = nil;
+            }
           }
         } else {
           // Type doesn't matter, it is a GPB*Dictionary.
diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
index 7ce5d54..919d007 100644
--- a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
@@ -57,6 +57,7 @@
 		F47476E51D21A524007C7B1A /* Duration.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248D41A92826400BC1EC6 /* Duration.pbobjc.m */; };
 		F47476E61D21A524007C7B1A /* Timestamp.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248D61A92826400BC1EC6 /* Timestamp.pbobjc.m */; };
 		F4B51B1E1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4B51B1D1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm */; };
+		F4C4B9E41E1D976300D3B61D /* GPBDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */; };
 		F4E675971B21D0000054530B /* Any.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675871B21D0000054530B /* Any.pbobjc.m */; };
 		F4E675991B21D0000054530B /* Api.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675891B21D0000054530B /* Api.pbobjc.m */; };
 		F4E6759B1B21D0000054530B /* Empty.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E6758B1B21D0000054530B /* Empty.pbobjc.m */; };
@@ -187,6 +188,7 @@
 		F4B6B8B21A9CCBDA00892426 /* GPBUnknownFieldSet_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBUnknownFieldSet_PackagePrivate.h; sourceTree = "<group>"; };
 		F4B6B8B61A9CD1DE00892426 /* GPBExtensionInternals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBExtensionInternals.h; sourceTree = "<group>"; };
 		F4B6B8B81A9CD1DE00892426 /* GPBRootObject_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRootObject_PackagePrivate.h; sourceTree = "<group>"; };
+		F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDictionaryTests.m; sourceTree = "<group>"; };
 		F4CF31701B162ED800BD9B06 /* unittest_objc_startup.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = unittest_objc_startup.proto; sourceTree = "<group>"; };
 		F4E675861B21D0000054530B /* Any.pbobjc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Any.pbobjc.h; path = google/protobuf/Any.pbobjc.h; sourceTree = "<group>"; };
 		F4E675871B21D0000054530B /* Any.pbobjc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = google/protobuf/Any.pbobjc.m; sourceTree = "<group>"; };
@@ -393,6 +395,7 @@
 				7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */,
 				5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */,
 				F4353D1C1AB8822D005A6198 /* GPBDescriptorTests.m */,
+				F4C4B9E21E1D974F00D3B61D /* GPBDictionaryTests.m */,
 				F4353D2C1AC06F10005A6198 /* GPBDictionaryTests.pddm */,
 				F4353D2D1AC06F10005A6198 /* GPBDictionaryTests+Bool.m */,
 				F4353D2E1AC06F10005A6198 /* GPBDictionaryTests+Int32.m */,
@@ -677,6 +680,7 @@
 				F4353D371AC06F10005A6198 /* GPBDictionaryTests+String.m in Sources */,
 				F4353D381AC06F10005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
 				8BBEA4B7147C727D00C4ADB7 /* GPBUtilitiesTests.m in Sources */,
+				F4C4B9E41E1D976300D3B61D /* GPBDictionaryTests.m in Sources */,
 				8BBEA4B8147C727D00C4ADB7 /* GPBWireFormatTests.m in Sources */,
 				8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */,
 				8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */,
diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
index 5f59971..64fc45c 100644
--- a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
@@ -65,6 +65,7 @@
 		F47476E91D21A537007C7B1A /* Duration.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248DE1A929C7D00BC1EC6 /* Duration.pbobjc.m */; };
 		F47476EA1D21A537007C7B1A /* Timestamp.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B4248E01A929C7D00BC1EC6 /* Timestamp.pbobjc.m */; };
 		F4B51B1C1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4B51B1B1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm */; };
+		F4C4B9E71E1D97BF00D3B61D /* GPBDictionaryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */; };
 		F4E675D01B21D1620054530B /* Any.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675B71B21D1440054530B /* Any.pbobjc.m */; };
 		F4E675D11B21D1620054530B /* Api.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675B91B21D1440054530B /* Api.pbobjc.m */; };
 		F4E675D21B21D1620054530B /* Empty.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = F4E675BC1B21D1440054530B /* Empty.pbobjc.m */; };
@@ -209,6 +210,7 @@
 		F4B6B8B11A9CCBBB00892426 /* GPBUnknownFieldSet_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBUnknownFieldSet_PackagePrivate.h; sourceTree = "<group>"; };
 		F4B6B8B31A9CD1C600892426 /* GPBExtensionInternals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBExtensionInternals.h; sourceTree = "<group>"; };
 		F4B6B8B51A9CD1C600892426 /* GPBRootObject_PackagePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBRootObject_PackagePrivate.h; sourceTree = "<group>"; };
+		F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDictionaryTests.m; sourceTree = "<group>"; };
 		F4CF31711B162EF500BD9B06 /* unittest_objc_startup.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = unittest_objc_startup.proto; sourceTree = "<group>"; };
 		F4E675B61B21D1440054530B /* Any.pbobjc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Any.pbobjc.h; path = google/protobuf/Any.pbobjc.h; sourceTree = "<group>"; };
 		F4E675B71B21D1440054530B /* Any.pbobjc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = Any.pbobjc.m; path = google/protobuf/Any.pbobjc.m; sourceTree = "<group>"; };
@@ -431,6 +433,7 @@
 				7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */,
 				5102DABB1891A052002037B6 /* GPBConcurrencyTests.m */,
 				F4353D1E1AB88243005A6198 /* GPBDescriptorTests.m */,
+				F4C4B9E51E1D97BB00D3B61D /* GPBDictionaryTests.m */,
 				F4353D3A1AC06F31005A6198 /* GPBDictionaryTests.pddm */,
 				F4353D3B1AC06F31005A6198 /* GPBDictionaryTests+Bool.m */,
 				F4353D3C1AC06F31005A6198 /* GPBDictionaryTests+Int32.m */,
@@ -773,6 +776,7 @@
 				F4353D451AC06F31005A6198 /* GPBDictionaryTests+String.m in Sources */,
 				F4353D461AC06F31005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
 				8BBEA4B7147C727D00C4ADB7 /* GPBUtilitiesTests.m in Sources */,
+				F4C4B9E71E1D97BF00D3B61D /* GPBDictionaryTests.m in Sources */,
 				8BBEA4B8147C727D00C4ADB7 /* GPBWireFormatTests.m in Sources */,
 				8BD3981F14BE59D70081D629 /* GPBUnittestProtos.m in Sources */,
 				8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */,
diff --git a/objectivec/Tests/GPBArrayTests.m b/objectivec/Tests/GPBArrayTests.m
index 0fb15e4..31f7550 100644
--- a/objectivec/Tests/GPBArrayTests.m
+++ b/objectivec/Tests/GPBArrayTests.m
@@ -32,6 +32,7 @@
 #import <XCTest/XCTest.h>
 
 #import "GPBArray.h"
+#import "GPBArray_PackagePrivate.h"
 
 #import "GPBTestUtilities.h"
 
@@ -3436,3 +3437,175 @@
 }
 
 @end
+
+#pragma mark - GPBAutocreatedArray Tests
+
+// These are hand written tests to double check some behaviors of the
+// GPBAutocreatedArray.
+
+// NOTE: GPBAutocreatedArray is private to the library, users of the library
+// should never have to directly deal with this class.
+
+@interface GPBAutocreatedArrayTests : XCTestCase
+@end
+
+@implementation GPBAutocreatedArrayTests
+
+- (void)testEquality {
+  GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+
+  XCTAssertTrue([array isEqual:@[]]);
+  XCTAssertTrue([array isEqualToArray:@[]]);
+
+  XCTAssertFalse([array isEqual:@[ @"foo" ]]);
+  XCTAssertFalse([array isEqualToArray:@[ @"foo" ]]);
+
+  [array addObject:@"foo"];
+
+  XCTAssertFalse([array isEqual:@[]]);
+  XCTAssertFalse([array isEqualToArray:@[]]);
+  XCTAssertTrue([array isEqual:@[ @"foo" ]]);
+  XCTAssertTrue([array isEqualToArray:@[ @"foo" ]]);
+  XCTAssertFalse([array isEqual:@[ @"bar" ]]);
+  XCTAssertFalse([array isEqualToArray:@[ @"bar" ]]);
+
+  GPBAutocreatedArray *array2 = [[GPBAutocreatedArray alloc] init];
+
+  XCTAssertFalse([array isEqual:array2]);
+  XCTAssertFalse([array isEqualToArray:array2]);
+
+  [array2 addObject:@"bar"];
+  XCTAssertFalse([array isEqual:array2]);
+  XCTAssertFalse([array isEqualToArray:array2]);
+
+  [array2 replaceObjectAtIndex:0 withObject:@"foo"];
+  XCTAssertTrue([array isEqual:array2]);
+  XCTAssertTrue([array isEqualToArray:array2]);
+
+  [array2 release];
+  [array release];
+}
+
+- (void)testCopy {
+  {
+    GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+
+    NSArray *cpy = [array copy];
+    XCTAssertTrue(cpy != array); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)0);
+
+    NSArray *cpy2 = [array copy];
+    XCTAssertTrue(cpy2 != array); // Ptr compare
+    // Can't compare cpy and cpy2 because NSArray has a singleton empty
+    // array it uses, so the ptrs are the same.
+    XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)0);
+
+    [cpy2 release];
+    [cpy release];
+    [array release];
+  }
+
+  {
+    GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+
+    NSMutableArray *cpy = [array mutableCopy];
+    XCTAssertTrue(cpy != array); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSMutableArray class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)0);
+
+    NSMutableArray *cpy2 = [array mutableCopy];
+    XCTAssertTrue(cpy2 != array); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSMutableArray class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)0);
+
+    [cpy2 release];
+    [cpy release];
+    [array release];
+  }
+
+  {
+    GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+    [array addObject:@"foo"];
+    [array addObject:@"bar"];
+
+    NSArray *cpy = [array copy];
+    XCTAssertTrue(cpy != array); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy[0], @"foo");
+    XCTAssertEqualObjects(cpy[1], @"bar");
+
+    NSArray *cpy2 = [array copy];
+    XCTAssertTrue(cpy2 != array); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy2[0], @"foo");
+    XCTAssertEqualObjects(cpy2[1], @"bar");
+
+    [cpy2 release];
+    [cpy release];
+    [array release];
+  }
+
+  {
+    GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+    [array addObject:@"foo"];
+    [array addObject:@"bar"];
+
+    NSMutableArray *cpy = [array mutableCopy];
+    XCTAssertTrue(cpy != array); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy[0], @"foo");
+    XCTAssertEqualObjects(cpy[1], @"bar");
+
+    NSMutableArray *cpy2 = [array mutableCopy];
+    XCTAssertTrue(cpy2 != array); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSArray class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedArray class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy2[0], @"foo");
+    XCTAssertEqualObjects(cpy2[1], @"bar");
+
+    [cpy2 release];
+    [cpy release];
+    [array release];
+  }
+}
+
+- (void)testIndexedSubscriptSupport {
+  // The base NSArray/NSMutableArray behaviors for *IndexedSubscript methods
+  // should still work via the methods that one has to override to make an
+  // NSMutableArray subclass.  i.e. - this should "just work" and if these
+  // crash/fail, then something is wrong in how NSMutableArray is subclassed.
+
+  GPBAutocreatedArray *array = [[GPBAutocreatedArray alloc] init];
+
+  [array addObject:@"foo"];
+  [array addObject:@"bar"];
+  XCTAssertEqual(array.count, (NSUInteger)2);
+  XCTAssertEqualObjects(array[0], @"foo");
+  XCTAssertEqualObjects(array[1], @"bar");
+  array[0] = @"foo2";
+  array[2] = @"baz";
+  XCTAssertEqual(array.count, (NSUInteger)3);
+  XCTAssertEqualObjects(array[0], @"foo2");
+  XCTAssertEqualObjects(array[1], @"bar");
+  XCTAssertEqualObjects(array[2], @"baz");
+
+  [array release];
+}
+
+@end
diff --git a/objectivec/Tests/GPBDictionaryTests.m b/objectivec/Tests/GPBDictionaryTests.m
new file mode 100644
index 0000000..52b4b32
--- /dev/null
+++ b/objectivec/Tests/GPBDictionaryTests.m
@@ -0,0 +1,186 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#import <Foundation/Foundation.h>
+#import <XCTest/XCTest.h>
+
+#import "GPBDictionary.h"
+#import "GPBDictionary_PackagePrivate.h"
+
+#import "GPBTestUtilities.h"
+
+#pragma mark - GPBAutocreatedDictionary Tests
+
+// These are hand written tests to double check some behaviors of the
+// GPBAutocreatedDictionary.  The GPBDictionary+[type]Tests files are generate
+// tests.
+
+// NOTE: GPBAutocreatedDictionary is private to the library, users of the
+// library should never have to directly deal with this class.
+
+@interface GPBAutocreatedDictionaryTests : XCTestCase
+@end
+
+@implementation GPBAutocreatedDictionaryTests
+
+- (void)testEquality {
+  GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
+
+  XCTAssertTrue([dict isEqual:@{}]);
+  XCTAssertTrue([dict isEqualToDictionary:@{}]);
+
+  XCTAssertFalse([dict isEqual:@{ @"foo" : @"bar" }]);
+  XCTAssertFalse([dict isEqualToDictionary:@{ @"foo" : @"bar" }]);
+
+  [dict setObject:@"bar" forKey:@"foo"];
+
+  XCTAssertFalse([dict isEqual:@{}]);
+  XCTAssertFalse([dict isEqualToDictionary:@{}]);
+  XCTAssertTrue([dict isEqual:@{ @"foo" : @"bar" }]);
+  XCTAssertTrue([dict isEqualToDictionary:@{ @"foo" : @"bar" }]);
+  XCTAssertFalse([dict isEqual:@{ @"bar" : @"baz" }]);
+  XCTAssertFalse([dict isEqualToDictionary:@{ @"bar" : @"baz" }]);
+
+  GPBAutocreatedDictionary *dict2 = [[GPBAutocreatedDictionary alloc] init];
+
+  XCTAssertFalse([dict isEqual:dict2]);
+  XCTAssertFalse([dict isEqualToDictionary:dict2]);
+
+  [dict2 setObject:@"mumble" forKey:@"foo"];
+  XCTAssertFalse([dict isEqual:dict2]);
+  XCTAssertFalse([dict isEqualToDictionary:dict2]);
+
+  [dict2 setObject:@"bar" forKey:@"foo"];
+  XCTAssertTrue([dict isEqual:dict2]);
+  XCTAssertTrue([dict isEqualToDictionary:dict2]);
+
+  [dict2 release];
+  [dict release];
+}
+
+- (void)testCopy {
+  {
+    GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
+
+    NSDictionary *cpy = [dict copy];
+    XCTAssertTrue(cpy != dict); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSDictionary class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)0);
+
+    NSDictionary *cpy2 = [dict copy];
+    XCTAssertTrue(cpy2 != dict); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSDictionary class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)0);
+
+    [cpy2 release];
+    [cpy release];
+    [dict release];
+  }
+
+  {
+    GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
+
+    NSMutableDictionary *cpy = [dict mutableCopy];
+    XCTAssertTrue(cpy != dict); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSMutableDictionary class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)0);
+
+    NSMutableDictionary *cpy2 = [dict mutableCopy];
+    XCTAssertTrue(cpy2 != dict); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSMutableDictionary class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)0);
+
+    [cpy2 release];
+    [cpy release];
+    [dict release];
+  }
+
+  {
+    GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
+    dict[@"foo"] = @"bar";
+    dict[@"baz"] = @"mumble";
+
+    NSDictionary *cpy = [dict copy];
+    XCTAssertTrue(cpy != dict); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSDictionary class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy[@"foo"], @"bar");
+    XCTAssertEqualObjects(cpy[@"baz"], @"mumble");
+
+    NSDictionary *cpy2 = [dict copy];
+    XCTAssertTrue(cpy2 != dict); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSDictionary class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy2[@"foo"], @"bar");
+    XCTAssertEqualObjects(cpy2[@"baz"], @"mumble");
+
+    [cpy2 release];
+    [cpy release];
+    [dict release];
+  }
+
+  {
+    GPBAutocreatedDictionary *dict = [[GPBAutocreatedDictionary alloc] init];
+    dict[@"foo"] = @"bar";
+    dict[@"baz"] = @"mumble";
+
+    NSMutableDictionary *cpy = [dict mutableCopy];
+    XCTAssertTrue(cpy != dict); // Ptr compare
+    XCTAssertTrue([cpy isKindOfClass:[NSMutableDictionary class]]);
+    XCTAssertFalse([cpy isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy[@"foo"], @"bar");
+    XCTAssertEqualObjects(cpy[@"baz"], @"mumble");
+
+    NSMutableDictionary *cpy2 = [dict mutableCopy];
+    XCTAssertTrue(cpy2 != dict); // Ptr compare
+    XCTAssertTrue(cpy2 != cpy); // Ptr compare
+    XCTAssertTrue([cpy2 isKindOfClass:[NSMutableDictionary class]]);
+    XCTAssertFalse([cpy2 isKindOfClass:[GPBAutocreatedDictionary class]]);
+    XCTAssertEqual(cpy2.count, (NSUInteger)2);
+    XCTAssertEqualObjects(cpy2[@"foo"], @"bar");
+    XCTAssertEqualObjects(cpy2[@"baz"], @"mumble");
+
+    [cpy2 release];
+    [cpy release];
+    [dict release];
+  }
+}
+
+@end
diff --git a/objectivec/Tests/GPBMessageTests.m b/objectivec/Tests/GPBMessageTests.m
index a3c5a6b..c15535c 100644
--- a/objectivec/Tests/GPBMessageTests.m
+++ b/objectivec/Tests/GPBMessageTests.m
@@ -1082,6 +1082,20 @@
   [repeatedStringArray release];
 }
 
+- (void)testSetOverAutocreatedArrayAndSetAgain {
+  // Ensure when dealing with replacing an array it is handled being either
+  // an autocreated one or a straight NSArray.
+
+  // The real test here is that nothing crashes while doing the work.
+  TestAllTypes *message = [TestAllTypes message];
+  [message.repeatedStringArray addObject:@"foo"];
+  XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)1);
+  message.repeatedStringArray = [NSMutableArray arrayWithObjects:@"bar", @"bar2", nil];
+  XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)2);
+  message.repeatedStringArray = [NSMutableArray arrayWithObject:@"baz"];
+  XCTAssertEqual(message.repeatedStringArray_Count, (NSUInteger)1);
+}
+
 - (void)testReplaceAutocreatedArray {
   // Replacing array should orphan the old one and cause its creator to become
   // visible.
@@ -1281,6 +1295,23 @@
   [strToStr release];
 }
 
+- (void)testSetOverAutocreatedMapAndSetAgain {
+  // Ensure when dealing with replacing a map it is handled being either
+  // an autocreated one or a straight NSDictionary.
+
+  // The real test here is that nothing crashes while doing the work.
+  TestRecursiveMessageWithRepeatedField *message =
+      [TestRecursiveMessageWithRepeatedField message];
+  message.strToStr[@"foo"] = @"bar";
+  XCTAssertEqual(message.strToStr_Count, (NSUInteger)1);
+  message.strToStr =
+      [NSMutableDictionary dictionaryWithObjectsAndKeys:@"bar", @"key1", @"baz", @"key2", nil];
+  XCTAssertEqual(message.strToStr_Count, (NSUInteger)2);
+  message.strToStr =
+      [NSMutableDictionary dictionaryWithObject:@"baz" forKey:@"mumble"];
+  XCTAssertEqual(message.strToStr_Count, (NSUInteger)1);
+}
+
 - (void)testReplaceAutocreatedMap {
   // Replacing map should orphan the old one and cause its creator to become
   // visible.