Objective-C 对象
Objective-C 对象模型
首先要了解一下Objective-C中关于类和对象的定义,Cocoa中大部分对象都是NSObject的子类(NSProxy是一个例外),继承了NSObject的方法。NSObject定义如下:
@interface NSObject <NSObject> { Classisa; }
NSObject可见一个对象的内存布局中第一个元素是指向类结构Class的isa指针。Class类结构定义如下:
typedef struct objc_class *Class; typedef struct objc_object { Class isa; } *id;
Class 是类结构体的别名,而id是一个objc_object对象结构体指针,objc_object内存布局中第一个元素是指向objc_class类结构体的指针。所以id可以指向任何内存布局以objc_class类结构体指针开始的对象。类结构体objc_class定义具体如下:
struct objc_class { Class isa; Class super_class const char *name long version long info long instance_size struct objc_ivar_list *ivars struct objc_method_list **methodLists struct objc_cache *cache struct objc_protocol_list *protocols }
objc_class类结构体的各成员介绍如下:
isa:是一个类结构体objc_class的指针。内存以objc_class为开始的数据类型对当做对象来看待,所以说类也是对象,普通的对象叫实例对象而类叫做类对象,类对象的isa指针指向的类叫做metaclass(元类)元类也是一个对象,元类的isa指向rootmetaclass(根元类), 根元类的isa指向本身,这样就成了一个闭环。类对象存储普通成员变量和普通方法,metaclass存储静态成员变量和静态方法,也就是类变量和类方法。
super_class: 指向父类的指针。类对象和元类对象有着同样的继承关系。关于继承和isa的关系如这篇文章所示。
name: C常量字符串,表示类的名字。可以在运行期,通过这个名称查找到该类例如id objc_getClass(constchar *aClassName)或该类的元类id objc_getMetaClass(const char*aClassName)。
version: 类版本信息。可以在运行期对其进行修改(class_setVersion)或获取(class_getVersion)。
info: 运行期使用的一些表示位。
CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含实例方法和变量;
CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
CLS_INITIALIZED(0x4L) 表示该类已经被运行期初始化了,这个标识位只被objc_addClass 所设置;
CLS_POSING (0x8L) 表示该类被 pose 成其他的类;(poseclass 在ObjC 2.0中被废弃了);
CLS_MAPPED (0x10L)为ObjC运行期所使用
CLS_FLUSH_CACHE(0x20L) 为ObjC运行期所使用
CLS_GROW_CACHE(0x40L) 为ObjC运行期所使用
CLS_NEED_BIND(0x80L) 为ObjC运行期所使用
CLS_METHOD_ARRAY(0x100L) 该标志位指示 methodlists 是指向一个 objc_method_list 还是一个包含 objc_method_list 指针的数组,methodlists指向指针的数组的意义是方便以Category的形式添加方法。
instance_size: 该类的实例变量大小(包括从父类继承下来的实例变量)。
ivars: 指向 objc_ivar_list 的指针,存储每个实例变量的内存地址,如果该类没有任何实例变量则为 NULL
methodLists: 与 info 的一些标志位有关,CLS_METHOD_ARRAY标识位决定其指向的东西(是指向单个objc_method_list还是一个 objc_method_list 指针数组),如果 info 设置了 CLS_CLASS 则 objc_method_list 存储实例方法,如果设置的是 CLS_META 则存储类方法。
cache:指向objc_cache的指针,用来缓存最近使用的方法,以提高方法调用过程中的查找速度。
protocols: 指向 objc_protocol_list 的指针,存储该类声明要遵守的正式协议。
动态添加属性(isa swizzling)
动态的添加属性主要在Category通过以下两个方法使两个对象关联起来。
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); id objc_getAssociatedObject(id object, const void *key);
关联策略policy有:
enum { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 };
具体例子如下:
#import <Foundation/Foundation.h> #include <objc/runtime.h> static const void *objectName = @"objectName"; @interface NSObject (Test) @property (nonatomic, strong) NSString *objectName; @end @implementation NSObject (Test) - (NSString *)objectName { return objc_getAssociatedObject(self, objectName); } - (void)setObjectName:(NSString *)name { objc_setAssociatedObject(self, objectName, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end int main (int argc, const char * argv[]) { @autoreleasepool { NSObject *object = [[NSObject alloc] init]; [object setObjectName:@"ObjectName"]; NSLog(@"%@",object.objectName); [object release]; } return 0; }
Method Swizzling
Method Swizzling可以搞定一件事情,那就是可以不通过继承来重写某个方法。而且还可以调用原来的方法。通常的做法是在category中添加一个方法(当然也可以是一个全新的class)。可以通过method_exchangeImplementations这个运行时方法来交换实现。示例如下:
#import <objc/runtime.h> @interface NSMutableArray (LoggingAddObject) - (void)logAddObject:(id)aObject; @end @implementation NSMutableArray (LoggingAddObject) + (void)load { Method addobject = class_getInstanceMethod(self, @selector(addObject:)); Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:)); method_exchangeImplementations(addObject, logAddObject); } - (void)logAddObject:(id)aobject { [self logAddObject:aObject]; NSLog(@"Added object %@ to array %@", aObject, self); } @end