Add GPBMessageDropUnknownFieldsRecursively() and tests.

GPBMessageDropUnknownFieldsRecursively() is a new helper to drop the
unknownFields from a message and all sub messages (in fields or extensions).
diff --git a/objectivec/GPBUtilities.h b/objectivec/GPBUtilities.h
index 52e7d2e..5464dfb 100644
--- a/objectivec/GPBUtilities.h
+++ b/objectivec/GPBUtilities.h
@@ -392,6 +392,11 @@
  **/
 NSData *GPBEmptyNSData(void) __attribute__((pure));
 
+/**
+ * Drops the `unknownFields` from the given message and from all sub message.
+ **/
+void GPBMessageDropUnknownFieldsRecursively(GPBMessage *message);
+
 NS_ASSUME_NONNULL_END
 
 CF_EXTERN_C_END
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index 68aadb7..1843478 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -58,6 +58,125 @@
   return defaultNSData;
 }
 
+void GPBMessageDropUnknownFieldsRecursively(GPBMessage *initialMessage) {
+  if (!initialMessage) {
+    return;
+  }
+
+  // Use an array as a list to process to avoid recursion.
+  NSMutableArray *todo = [NSMutableArray arrayWithObject:initialMessage];
+
+  while (todo.count) {
+    GPBMessage *msg = todo.lastObject;
+    [todo removeLastObject];
+
+    // Clear unknowns.
+    msg.unknownFields = nil;
+
+    // Handle the message fields.
+    GPBDescriptor *descriptor = [[msg class] descriptor];
+    for (GPBFieldDescriptor *field in descriptor->fields_) {
+      if (!GPBFieldDataTypeIsMessage(field)) {
+        continue;
+      }
+      switch (field.fieldType) {
+        case GPBFieldTypeSingle:
+          if (GPBGetHasIvarField(msg, field)) {
+            GPBMessage *fieldMessage = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+            [todo addObject:fieldMessage];
+          }
+          break;
+
+        case GPBFieldTypeRepeated: {
+          NSArray *fieldMessages = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+          if (fieldMessages.count) {
+            [todo addObjectsFromArray:fieldMessages];
+          }
+          break;
+        }
+
+        case GPBFieldTypeMap: {
+          id rawFieldMap = GPBGetObjectIvarWithFieldNoAutocreate(msg, field);
+          switch (field.mapKeyDataType) {
+            case GPBDataTypeBool:
+              [(GPBBoolObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  BOOL key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeFixed32:
+            case GPBDataTypeUInt32:
+              [(GPBUInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  uint32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeInt32:
+            case GPBDataTypeSFixed32:
+            case GPBDataTypeSInt32:
+              [(GPBInt32ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  int32_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeFixed64:
+            case GPBDataTypeUInt64:
+              [(GPBUInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  uint64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeInt64:
+            case GPBDataTypeSFixed64:
+            case GPBDataTypeSInt64:
+              [(GPBInt64ObjectDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  int64_t key, id _Nonnull object, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:object];
+              }];
+              break;
+            case GPBDataTypeString:
+              [(NSDictionary*)rawFieldMap enumerateKeysAndObjectsUsingBlock:^(
+                  NSString * _Nonnull key, GPBMessage * _Nonnull obj, BOOL * _Nonnull stop) {
+                #pragma unused(key, stop)
+                [todo addObject:obj];
+              }];
+              break;
+            case GPBDataTypeFloat:
+            case GPBDataTypeDouble:
+            case GPBDataTypeEnum:
+            case GPBDataTypeBytes:
+            case GPBDataTypeGroup:
+            case GPBDataTypeMessage:
+              NSCAssert(NO, @"Aren't valid key types.");
+          }
+          break;
+        }  // switch(field.mapKeyDataType)
+      }  // switch(field.fieldType)
+    }  // for(fields)
+
+    // Handle any extensions holding messages.
+    for (GPBExtensionDescriptor *extension in [msg extensionsCurrentlySet]) {
+      if (!GPBDataTypeIsMessage(extension.dataType)) {
+        continue;
+      }
+      if (extension.isRepeated) {
+        NSArray *extMessages = [msg getExtension:extension];
+        [todo addObjectsFromArray:extMessages];
+      } else {
+        GPBMessage *extMessage = [msg getExtension:extension];
+        [todo addObject:extMessage];
+      }
+    }  // for(extensionsCurrentlySet)
+
+  }  // while(todo.count)
+}
+
+
 // -- About Version Checks --
 // There's actually 3 places these checks all come into play:
 // 1. When the generated source is compile into .o files, the header check
diff --git a/objectivec/Tests/GPBUtilitiesTests.m b/objectivec/Tests/GPBUtilitiesTests.m
index dfaca66..2e206a5 100644
--- a/objectivec/Tests/GPBUtilitiesTests.m
+++ b/objectivec/Tests/GPBUtilitiesTests.m
@@ -39,6 +39,7 @@
 #import "GPBDescriptor.h"
 #import "GPBDescriptor_PackagePrivate.h"
 #import "GPBMessage.h"
+#import "GPBUnknownField_PackagePrivate.h"
 
 #import "google/protobuf/MapUnittest.pbobjc.h"
 #import "google/protobuf/Unittest.pbobjc.h"
@@ -197,4 +198,203 @@
   }
 }
 
+// Helper to make an unknown field set with something in it.
+static GPBUnknownFieldSet *UnknownFieldsSetHelper(int num) {
+  GPBUnknownFieldSet *result =
+      [[[GPBUnknownFieldSet alloc] init] autorelease];
+
+  GPBUnknownField *field =
+      [[[GPBUnknownField alloc] initWithNumber:num] autorelease];
+  [field addVarint:num];
+  [result addField:field];
+
+  return result;
+}
+
+- (void)testDropMessageUnknownFieldsRecursively {
+  TestAllExtensions *message = [TestAllExtensions message];
+
+  // Give it unknownFields.
+  message.unknownFields = UnknownFieldsSetHelper(777);
+
+  // Given it extensions that include a message with unknown fields of its own.
+  {
+    // Int
+    [message setExtension:[UnittestRoot optionalInt32Extension] value:@5];
+
+    // Group
+    OptionalGroup_extension *optionalGroup = [OptionalGroup_extension message];
+    optionalGroup.a = 123;
+    optionalGroup.unknownFields = UnknownFieldsSetHelper(779);
+    [message setExtension:[UnittestRoot optionalGroupExtension]
+                    value:optionalGroup];
+
+    // Message
+    TestAllTypes_NestedMessage *nestedMessage =
+        [TestAllTypes_NestedMessage message];
+    nestedMessage.bb = 456;
+    nestedMessage.unknownFields = UnknownFieldsSetHelper(778);
+    [message setExtension:[UnittestRoot optionalNestedMessageExtension]
+                    value:nestedMessage];
+
+    // Repeated Group
+    RepeatedGroup_extension *repeatedGroup =
+      [[RepeatedGroup_extension alloc] init];
+    repeatedGroup.a = 567;
+    repeatedGroup.unknownFields = UnknownFieldsSetHelper(780);
+    [message addExtension:[UnittestRoot repeatedGroupExtension]
+                    value:repeatedGroup];
+    [repeatedGroup release];
+
+    // Repeated Message
+    nestedMessage = [[TestAllTypes_NestedMessage alloc] init];
+    nestedMessage.bb = 678;
+    nestedMessage.unknownFields = UnknownFieldsSetHelper(781);
+    [message addExtension:[UnittestRoot repeatedNestedMessageExtension]
+                    value:nestedMessage];
+    [nestedMessage release];
+  }
+
+  // Confirm everything is there.
+
+  XCTAssertNotNil(message);
+  XCTAssertNotNil(message.unknownFields);
+  XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]);
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]);
+    OptionalGroup_extension *optionalGroup =
+        [message getExtension:[UnittestRoot optionalGroupExtension]];
+    XCTAssertNotNil(optionalGroup);
+    XCTAssertEqual(optionalGroup.a, 123);
+    XCTAssertNotNil(optionalGroup.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]);
+    TestAllTypes_NestedMessage *nestedMessage =
+        [message getExtension:[UnittestRoot optionalNestedMessageExtension]];
+    XCTAssertNotNil(nestedMessage);
+    XCTAssertEqual(nestedMessage.bb, 456);
+    XCTAssertNotNil(nestedMessage.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]);
+    NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]];
+    XCTAssertEqual(repeatedGroups.count, (NSUInteger)1);
+    RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject;
+    XCTAssertNotNil(repeatedGroup);
+    XCTAssertEqual(repeatedGroup.a, 567);
+    XCTAssertNotNil(repeatedGroup.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]);
+    NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]];
+    XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1);
+    TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject;
+    XCTAssertNotNil(repeatedNestedMessage);
+    XCTAssertEqual(repeatedNestedMessage.bb, 678);
+    XCTAssertNotNil(repeatedNestedMessage.unknownFields);
+  }
+
+  // Drop them.
+  GPBMessageDropUnknownFieldsRecursively(message);
+
+  // Confirm unknowns are gone from within the messages.
+
+  XCTAssertNotNil(message);
+  XCTAssertNil(message.unknownFields);
+  XCTAssertTrue([message hasExtension:[UnittestRoot optionalInt32Extension]]);
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot optionalGroupExtension]]);
+    OptionalGroup_extension *optionalGroup =
+        [message getExtension:[UnittestRoot optionalGroupExtension]];
+    XCTAssertNotNil(optionalGroup);
+    XCTAssertEqual(optionalGroup.a, 123);
+    XCTAssertNil(optionalGroup.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot optionalNestedMessageExtension]]);
+    TestAllTypes_NestedMessage *nestedMessage =
+        [message getExtension:[UnittestRoot optionalNestedMessageExtension]];
+    XCTAssertNotNil(nestedMessage);
+    XCTAssertEqual(nestedMessage.bb, 456);
+    XCTAssertNil(nestedMessage.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot repeatedGroupExtension]]);
+    NSArray *repeatedGroups = [message getExtension:[UnittestRoot repeatedGroupExtension]];
+    XCTAssertEqual(repeatedGroups.count, (NSUInteger)1);
+    RepeatedGroup_extension *repeatedGroup = repeatedGroups.firstObject;
+    XCTAssertNotNil(repeatedGroup);
+    XCTAssertEqual(repeatedGroup.a, 567);
+    XCTAssertNil(repeatedGroup.unknownFields);
+  }
+
+  {
+    XCTAssertTrue([message hasExtension:[UnittestRoot repeatedNestedMessageExtension]]);
+    NSArray *repeatedNestedMessages = [message getExtension:[UnittestRoot repeatedNestedMessageExtension]];
+    XCTAssertEqual(repeatedNestedMessages.count, (NSUInteger)1);
+    TestAllTypes_NestedMessage *repeatedNestedMessage = repeatedNestedMessages.firstObject;
+    XCTAssertNotNil(repeatedNestedMessage);
+    XCTAssertEqual(repeatedNestedMessage.bb, 678);
+    XCTAssertNil(repeatedNestedMessage.unknownFields);
+  }
+
+}
+
+- (void)testDropMessageUnknownFieldsRecursively_Maps {
+  TestMap *message = [TestMap message];
+
+  {
+    ForeignMessage *foreignMessage = [ForeignMessage message];
+    foreignMessage.unknownFields = UnknownFieldsSetHelper(100);
+    [message.mapInt32ForeignMessage setObject:foreignMessage forKey:100];
+
+    foreignMessage = [ForeignMessage message];
+    foreignMessage.unknownFields = UnknownFieldsSetHelper(101);
+    [message.mapStringForeignMessage setObject:foreignMessage forKey:@"101"];
+  }
+
+  // Confirm everything is there.
+
+  XCTAssertNotNil(message);
+
+  {
+    ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100];
+    XCTAssertNotNil(foreignMessage);
+    XCTAssertNotNil(foreignMessage.unknownFields);
+  }
+
+  {
+    ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"];
+    XCTAssertNotNil(foreignMessage);
+    XCTAssertNotNil(foreignMessage.unknownFields);
+  }
+
+  GPBMessageDropUnknownFieldsRecursively(message);
+
+  // Confirm unknowns are gone from within the messages.
+
+  XCTAssertNotNil(message);
+
+  {
+    ForeignMessage *foreignMessage = [message.mapInt32ForeignMessage objectForKey:100];
+    XCTAssertNotNil(foreignMessage);
+    XCTAssertNil(foreignMessage.unknownFields);
+  }
+
+  {
+    ForeignMessage *foreignMessage = [message.mapStringForeignMessage objectForKey:@"101"];
+    XCTAssertNotNil(foreignMessage);
+    XCTAssertNil(foreignMessage.unknownFields);
+  }
+
+}
+
 @end