aboutsummaryrefslogtreecommitdiff
path: root/AppKit/GTMIBArray.m
blob: a027440b5ad67e3753f3ffa1d27c524f08642845 (plain)
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//
//  GTMIBArray.m
//
//  Copyright 2009 Google Inc.
//
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License.  You may obtain a copy
//  of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
//  License for the specific language governing permissions and limitations under
//  the License.
//


#import "GTMIBArray.h"
#import "GTMDefines.h"

@implementation GTMIBArray

- (void)dealloc {
  [realArray_ release];
  [super dealloc];
}

- (void)setupRealArray {

#ifdef DEBUG
  // It is very easy to create a cycle if you are chaining these in IB, so in
  // debug builds, we try to catch this to inform the developer.  Use -[NSArray
  // indexOfObjectIdenticalTo:] to get pointer comparisons instead of object
  // equality.
  static NSMutableArray *ibArraysBuilding = nil;
  if (!ibArraysBuilding) {
    ibArraysBuilding = [[NSMutableArray alloc] init];
  }
  _GTMDevAssert([ibArraysBuilding indexOfObjectIdenticalTo:self] == NSNotFound,
                @"There is a cycle in your GTMIBArrays!");
  [ibArraysBuilding addObject:self];
#endif  // DEBUG

  // Build the array up.
  NSMutableArray *builder = [NSMutableArray array];
  Class ibArrayClass = [GTMIBArray class];
  id objs[] = {
    object1_, object2_, object3_, object4_, object5_,
  };
  for (size_t idx = 0 ; idx < sizeof(objs) / (sizeof(objs[0])) ; ++idx) {
    id obj = objs[idx];
    if (obj) {
      if ([obj isKindOfClass:ibArrayClass]) {
        [builder addObjectsFromArray:obj];
      } else {
        [builder addObject:obj];
      }
    }
  }

#ifdef DEBUG
  [ibArraysBuilding removeObject:self];
#endif  // DEBUG

  // Now copy with our zone.
  realArray_ = [builder copyWithZone:[self zone]];
}

// ----------------------------------------------------------------------------
// NSArray has two methods that everything else seems to work on, simply
// implement those.

- (NSUInteger)count {
  if (!realArray_) [self setupRealArray];
  return [realArray_ count];
}

- (id)objectAtIndex:(NSUInteger)idx {
  if (!realArray_) [self setupRealArray];
  return [realArray_ objectAtIndex:idx];
}

// ----------------------------------------------------------------------------
// Directly relay the enumeration based calls just in case there is some extra
// efficency to be had.

- (NSEnumerator *)objectEnumerator {
  if (!realArray_) [self setupRealArray];
  return [realArray_ objectEnumerator];
}

- (NSEnumerator *)reverseObjectEnumerator {
  if (!realArray_) [self setupRealArray];
  return [realArray_ reverseObjectEnumerator];
}

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
                                  objects:(id *)stackbuf
                                    count:(NSUInteger)len {
  if (!realArray_) [self setupRealArray];
  return [realArray_ countByEnumeratingWithState:state
                                         objects:stackbuf
                                           count:len];
}

#endif  // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5

// ----------------------------------------------------------------------------
// Directly relay the copy methods, again, for any extra efficency.

- (id)copyWithZone:(NSZone *)zone {
  if (!realArray_) [self setupRealArray];
  return [realArray_ copyWithZone:zone];
}

- (id)mutableCopyWithZone:(NSZone *)zone {
  if (!realArray_) [self setupRealArray];
  return [realArray_ mutableCopyWithZone:zone];
}

// ----------------------------------------------------------------------------
// On 10.6, being loaded out of a nib causes the object to get hashed and
// stored off.  The implementation of -hash in NSArray then calls -count, which
// causes this object to latch on to an empty array.  So...
// 1. -hash gets overridden to simply use the class pointer to maintain
//    the -[NSObject hash] contract that equal objects must have the same hash
//    value.  This puts the work in isEqual...
// 2. -isEqual: overide.  Objects can't use the NSArray version until all of
//    the outlets have been filled in and the object is truly setup. The best
//    escape for this is to simply do pointer comparison until the outlets are
//    fully setup.
// 3. awakeFromNib gets overridden to force the initialize of the real array
//    when all the outlets have been filled in.
//
// NOTE: The first attempt was to just overide hash, but that only makes the
// first IBArray in a nib work. The fact that isEqual was falling through to
// the NSArray version (comparing to empty arrays), prevented all of the
// IBArrays from being fully loaded from the nib correctly.

- (NSUInteger)hash {
  return (NSUInteger)(void*)[self class];
}

- (BOOL)isEqual:(id)anObject {
  if ([anObject isMemberOfClass:[self class]]) {
    GTMIBArray *ibArray2 = anObject;
    if (!realArray_ || !(ibArray2->realArray_)) {
      // If realArray_ or ibArray2 haven't been fully configured yet, the only
      // way they can be equal is if they are the same pointer.
      return (self == anObject);
    }
  }
  return [super isEqual:anObject];
}

- (void)awakeFromNib {
  [realArray_ autorelease];
  realArray_ = nil;
  [self setupRealArray];
}

@end