isa 指针不再被推荐使用,但是 KVO 的实现却是在改变它的指向实现的,即 addObserver 时创建其子类,并将 isa 指向新子类。
mikeash.com: Friday Q&A 2009-01-23
ios - isa pointer in objective-c - Stack Overflow
//
// main.m
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <objc/runtime.h>
@interface TestClass : NSObject
{
int x;
int y;
int z;
}
@property int x;
@property int y;
@property int z;
@end
@implementation TestClass
@synthesize x, y, z;
@end
@interface TestClass (MyCategory)
@property(nonatomic, assign) id w;
@end
@implementation TestClass (MyCategory)
- (id)w {
return objc_getAssociatedObject(self, @selector(w));
}
- (void)setW:(id)w {
objc_setAssociatedObject(self, @selector(w), w, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
static NSArray *ClassMethodNames(Class c)
{
NSMutableArray *array = [NSMutableArray array];
unsigned int methodCount = 0;
Method *methodList = class_copyMethodList(c, &methodCount);
unsigned int i;
for(i = 0; i < methodCount; i++)
[array addObject: NSStringFromSelector(method_getName(methodList[i]))];
free(methodList);
return array;
}
static void PrintDescription(NSString *name, id obj)
{
// id s = obj->isa;
// id s = [obj class]; // 与下面的答案不同,如果 obj 被 KVO 的话
id s = object_getClass(obj);
NSString *str = [NSString stringWithFormat:
@"%@: %@\n\tNSObject class %s\n\tlibobjc class %s\n\timplements methods <%@>",
name,
obj,
class_getName([obj class]),
class_getName(s),
[ClassMethodNames(s) componentsJoinedByString:@", "]];
printf("%s\n", [str UTF8String]);
}
int main(int argc, char * argv[]) {
TestClass *x = [[TestClass alloc] init];
TestClass *y = [[TestClass alloc] init];
TestClass *xy = [[TestClass alloc] init];
TestClass *control = [[TestClass alloc] init];
[x addObserver:x forKeyPath:@"x" options:0 context:NULL];
[xy addObserver:xy forKeyPath:@"x" options:0 context:NULL];
[y addObserver:y forKeyPath:@"y" options:0 context:NULL];
[xy addObserver:xy forKeyPath:@"y" options:0 context:NULL];
PrintDescription(@"control", control);
PrintDescription(@"x", x);
PrintDescription(@"y", y);
PrintDescription(@"xy", xy);
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(TestClass.self, &count);
NSMutableString *ivarNames = NSMutableString.new;
for (NSInteger i = 0; i < count; ++i) { // count 为 3,也就是 w 并没被计入
[ivarNames appendFormat:@"%s, ", ivar_getName(ivars[i])];
}
free(ivars);
NSLog(@"TestClass's associated property: (%u) %@", count, ivarNames);
printf("Using NSObject methods, normal setX: is %p, overridden setX: is %p\n",
[control methodForSelector:@selector(setX:)],
[x methodForSelector:@selector(setX:)]);
printf("Using libobjc functions, normal setX: is %p, overridden setX: is %p\n",
method_getImplementation(class_getInstanceMethod(object_getClass(control),
@selector(setX:))),
method_getImplementation(class_getInstanceMethod(object_getClass(x),
@selector(setX:))));
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}