Reference links: blog.sunnyxx.com/2014/12/18/…

We all know that swizzle method is an interesting and useful technique in OC. Now suppose we wanted to hook NSMutableArray insertObject:atIndex: method, we would say in the classification of NSMutableArray:

swizzleInstanceMethod([self class], @selector(insertObject:atIndex:), @selector(swizzle_insertObject:atIndex:));
Copy the code

However, we can see that although Swizzle succeeds, it does not actually execute the swizzle_insertObject:atIndex: method. Why is this?

Let’s write a test code:

NSString *nilStr = nil;
[[NSMutableArray array] addObject:nilStr];
Copy the code

Look at the error message below:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Copy the code

What the hell is __NSArrayM? This is why swizzle_insertObject:atIndex: is not executed after swizzle method.

Take a look at the following print:

(lldb) po [[NSMutableArray alloc] init]
<__NSArrayM 0x7fb452e04a20>(

)

(lldb) po [NSMutableArray array]
<__NSArrayM 0x7fb452d0ba30>(

)

(lldb) po [NSMutableArray arrayWithObject:@""]; <__NSArrayM 0x7fb452d0baf0>( ) (lldb) po [[NSMutableArray alloc] class] __NSPlaceholderArray (lldb) po [NSArray array] <__NSArray0 0x7fb452d05910>( ) (lldb) po [[NSArray alloc] init] <__NSArray0 0x7fb452d05910>( ) (lldb) po @[] <__NSArray0  0x7fb452d05910>( ) (lldb) po [[NSArray alloc] class] __NSPlaceholderArray (lldb) po [[NSArray alloc] initWithObjects:@"", nil];
<__NSArrayI 0x7fb452f059c0>(

)

(lldb) 
Copy the code

Take a closer look at the clues,

[NSMutableArray array] // __NSArrayM type (mutable) [NSArray array] // __NSArray0 type (empty IMmutable) [[NSArray alloc] initWithObjects:@"". NSMutableArray alloc] // __NSPlaceholderArray [[NSMutableArray alloc] class] // __NSPlaceholderArrayCopy the code

As you can see, these print types are the actual Array types that Apple is hiding, so we hook the [NSMutableArray class] method at the beginning of this article, not the __NSArrayM method that we actually call in the project. So we didn’t execute our swizzle_method. (One small detail: the empty immutable group __NSArray0 printed in all three ways points to the same address)

So how do you solve this problem? Now that we know which type the source method belongs to, we’ll apply swizzleMethod to that type:

NSMutableArray *mutArray = [NSMutableArray array];
swizzleInstanceMethod([mutArray class], @selector(insertObject:atIndex:), @selector(swizzle_insertObject:atIndex:));
Copy the code

}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} We’ll need it if we want to hook NSArray init.

NSArray *placeholderArray = [NSArray alloc];
swizzleInstanceMethod([placeholderArray class], @selector(initWithObjects:count:), @selector(swizzle_initWithObjects:count:));
Copy the code

It’s not just NSArray, there’s also NSDictionary, NSString, NSNumber in Foundation, so if you want to hook these types of methods, you have to pay attention to which subtype the source method actually belongs to.