The reason for customizing static arrays
For one reason, the Objective-C guy didn’t have one, so he had to build his own.
Internal implementation scheme
Create an array object, JKRArray, that inherits directly from NSObject. First of all, the custom array should be compatible with the Objective-C native language, so our static array should hold Pointers to objects just like NSArray, except that the custom static array needs to be initialized with a specified length, and when initialized, it’s an empty array, In addition, elements can be directly inserted and removed by subscript at any position within the array length. For example, after initializing an array of length 3, the internal structure of the array should be:
[nil, nil, nil]
Copy the code
After inserting an @1 directly into array[1] by subscript, the internal structure of the array should look like this:
[nil, 1, nil]
Copy the code
Because arrays need to hold Pointers to objects, and because of the limited functionality, we have to have our own static array to hold Pointers. Objective-c doesn’t have this functionality, so we can only use C++ arrays to do this. Change the jkrarray. m file to jkrarray. mm and add -fno-objC-arc to the Compiler Flags of jkrarray. mm in the TARGETS- buildPhase-compile Sources of the project.
Object encapsulation
Here, try to imitate the interface design style of the official Objective-C language to achieve the purpose of easy to remember the interface and easy to use.
{
@protected
void ** _array;
NSUInteger _length;
}
- (instancetype)init __unavailable;
+ (instancetype)new __unavailable;
+ (instancetype)arrayWithLength:(NSUInteger)length;
- (instancetype)initWithLength:(NSUInteger)length;
- (NSUInteger)length;
- (void)setObject:(nullable ObjectType)object AtIndex:(NSUInteger)index;
- (nullable ObjectType)objectAtIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfObject:(nullable ObjectType)object;
- (BOOL)containsObject:(ObjectType)object;
@end
@interface JKRArray<ObjectType> (JKRExtendedArray)
- (void)enumerateObjectsUsingBlock:(void (^)(_Nullable ObjectType obj, NSUInteger idx, BOOL *stop))block;
- (_Nullable ObjectType)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(_Nullable ObjectType)obj atIndexedSubscript:(NSUInteger)idx;
@end
@interface JKRArray<ObjectType> (NSGenericFastEnumeraiton) <NSFastEnumeration>
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;
@end
Copy the code
Interface definition and function, and the official NSArray all maintain the same, so most of the interface will not explain, here one by one to illustrate some NSArray exist single and not commonly used and our custom interface.
{// The access to the following two member variables is set to private, because they should not be changed @private // The pointer to the object added to the array void ** _array; // The length of the array NSUInteger _length; } // The following is defined so that our array can only be created by specifying a length, since static data must be specified with a length - (instanceType)init __unavailable; + (instancetype)new __unavailable; + (instancetype)arrayWithLength:(NSUInteger)length; - (instancetype)initWithLength:(NSUInteger)length; /// The following two methods are declared and implemented so that our custom array class can be implemented with [] values and assignments, for example id obj = array[10], array[3] = [NSObject new]. - (_Nullable ObjectType)objectAtIndexedSubscript:(NSUInteger)idx; - (void)setObject:(_Nullable ObjectType)obj atIndexedSubscript:(NSUInteger)idx; // The <NSFastEnumeration> protocol and this method are for implementationfor inRapid traverse - (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *) state objects: (id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len;Copy the code
Interface implementation
Initialize the interface implementation
At the time of initialization, an array of Pointers is created with the length passed in by the initializer.
#pragma mark - Initializes+ (instancetype)arrayWithLength:(NSUInteger)length {// manually managing class methods to create objects requires calling autoreleasereturn[[[self alloc] initWithLength:length] autorelease]; } - (instancetype)initWithLength:(NSUInteger)length { self = [super init]; _length = length; _array = new void*[length]();return self;
}
Copy the code
The border check
Encapsulate a method that checks for out of bounds before all methods on index are executed
#pragma mark - Border checking
- (void)checkRangeWithIndex:(NSUInteger)index {
if (index < 0 || index >= _length) NSAssert(NO, @"Index: %zd, Length: %zd", index, _length);
}
Copy the code
Add the element at index
Time complexity: O(1)
#pragma mark - Adds elements
- (void)setObject: (id) Object AtIndex: (NSUInteger) index {/ / check whether incoming index is outside the range of the array [self checkRangeWithIndex: index]; OldObject = [self objectAtIndex:index]; // If the added element is the same as the original element, it is returned directly.if (oldObject == object) return; // If the added element is a new element, the old element reference count is -1if(oldObject ! = nil) [oldObject release]; // Add new element reference count +1if(object) [object retain]; *(_array + index) = (__bridge void *)object; }Copy the code
Delete elements by index
Time complexity: O(1)
#pragma mark - Removes elements- (void) removeObjectAtIndex: (NSUInteger) index {/ / check whether the incoming index is more than the scope of the array [self checkRangeWithIndex: index]; Object = (__bridge id)(*(_array + index)); // Remove reference count -1 (+1 when added)if (object) [object release];
*(_array + index) = 0;
}
Copy the code
Get the element by index
Time complexity: O(1)
#pragma mark - Gets elements- (id)objectAtIndex:(NSUInteger)index { [self checkRangeWithIndex:index]; Id object = (__bridge ID)(*(_array + index));return object;
}
Copy the code
Enumerate all elements
#pragma mark - enumeration- (void)enumerateObjectsUsingBlock:(void (^)(id _Nullable, NSUInteger, BOOL * _Nonnull))block { BOOL stop = NO; // Traverses all index values in the length rangefor (NSUInteger i = 0; i < _length && !stop; i++) {
id object = [self objectAtIndex:i];
block(object, i, &stop);
}
}
Copy the code
Gets the index of the element
Time complexity: O(N)
#pragma mark - The first subscript of the element stored in the array- (NSUInteger)indexOfObject:(id)object {// if no index is found, __block NSUInteger index = NSUIntegerMax; __block NSUInteger index = NSUIntegerMax; // Start with index 0, Find the first meet the index [self enumerateObjectsUsingBlock: ^ (id _Nullable obj, NSUInteger independence idx, BOOL * _Nonnull stop) {if (object == obj) {
index = idx;
*stop = YES;
} else if([object isEqual:obj]){ index = idx; *stop = YES; }}];return index;
}
Copy the code
Returns whether an array contains an element
Time complexity: O(N)
#pragma mark - Whether to include- (BOOL)containsObject:(id)object {// check the index of the element, NSUInteger index = [self indexOfObject:object];return index < _length;
}
Copy the code
Return array length
Time complexity: O(1)
#pragma mark - Returns the length
- (NSUInteger)length {
return _length;
}
Copy the code
Support for array operators
#pragma mark - Supports array operators
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
return [self objectAtIndex:idx];
}
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx {
[self setObject:obj AtIndex:idx];
}
Copy the code
Support for in fast traversal
#pragma mark - Fast traversal
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable [])buffer count:(NSUInteger)len {
if (state->state > 0) return 0;
state->mutationsPtr = (unsigned long*)self;
NSUInteger retCount = state->extra[0];
state->itemsPtr = (id *)(_array + state->extra[1]);
if (retCount == 0) retCount = _length;
if (retCount > len) {
state->extra[0] = retCount - len;
state->extra[1] += len;
retCount = len;
} else {
state->state++;
}
return retCount;
}
Copy the code
Override dealloc
#pragma mark - dealloc- (void)dealloc {// Before dealloc, the element reference count must be restored (-1). Otherwise, memory leaks will occurfor (NSUInteger i = 0; i < _length; i++) {
id object = [self objectAtIndex:i];
if (object) [object release];
}
delete _array;
[super dealloc];
}
Copy the code
Overriding the description method
We’re going to make it possible for custom arrays to be printed as arrays, just like NSArray
- (NSString *)description {
NSMutableString *mutableString = [NSMutableString string];
[mutableString appendString:[NSString stringWithFormat:@"<%@: %p>: \nlength: %zd\n{\n", self.class, self, _length]];
for (NSUInteger i = 0; i < _length; i++) {
if (i) [mutableString appendString:@"\n"];
id object = [self objectAtIndex:i];
if (object) {
[mutableString appendString:@""];
[mutableString appendString:[object description]];
} else {
[mutableString appendString:@""];
[mutableString appendString:@"Null"];
}
}
[mutableString appendString:@"\n}"];
return mutableString;
}
Copy the code
Testing capabilities
Initialization test
JKRArray *array = [JKRArray arrayWithLength:6];
NSLog(@"% @", array); Print: <JKRArray: 0x102107CB0 >: length: 6 {Null Null Null Null Null Null}Copy the code
Insert the test
array[2] = [Person new];
NSLog(@"% @", array); Print: <JKRArray: 0x1006079f0>: Length: 6 {Null Null <Person: 0x10286bc60> Null Null Null}Copy the code
Delete the test
array[2] = nil;
NSLog(@"% @", array); <Person: 0x100713400> dealloc <JKRArray: 0x100607e20>: Length: 6 {Null Null Null Null Null Null Null}Copy the code
The source code
Click to view source code