NSObject
+ (void)initialize;
+ (void)load;
initialize方法在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次。initialize方法实际上是一种惰性调用,也就是说如果一个类一直没被用到,那它的initialize方法也不会被调用,这一点有利于节约资源。
load方法在这个文件被程序装载时调用。只要是在Compile Sources中出现的文件总是会被装载,这与这个类是否被用到无关,因此load方法总是在main函数之前调用。
总结:
1.load和initialize方法都会在实例化对象之前调用,以main函数为分水岭,前者在main函数之前调用,后者在之后调用。这两个方法会被自动调用,不能手动调用它们。
2.load和initialize方法都不用显示的调用父类的方法而是自动调用,即使子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类。
3.load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
4.load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。
+ (instancetype)alloc;
+ (instancetype)allocWithZone:(struct _NSZone *)zone;
使用alloc方法创建一个类的实例的时候,其实最后还是会调用 allocWithZone方法。在OC中allocWithZone这个方法应该是弃用了的,只是由于历史原因苹果还保留着这个函数。一切起源于Apple官方文档里面关于单例(Singleton)的示范代码。
- (instancetype)init;
初始化的对象
- (id)copy;
- (id)mutableCopy;
+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
HSPerson *p = [[HSPerson alloc] init];
p.age = 20;
p.height = 170.0;
HSPerson *copyP = [p copy];
//<NSCopying>
- (id)copyWithZone:(NSZone *)zone
{
HSPerson *person = [[HSPerson allocWithZone:zone] init];
return person;
// 有些人可能下面alloc,重新初始化空间,但这方法已给你分配了zone,自己就无需再次alloc内存空间了
// HSPerson *person = [[HSPerson alloc] init];
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
HSPerson *person = [[HSPerson allocWithZone:zone] init];
person.age = self.age;
person.height = self.height;
return person;
}
@property (nonatomic, copy) NSString *name; property copy实际上就对name干了这个:
- (void)setName:(NSString *)name
{
_name = [name copy];
}
- (void)dealloc;
对象销毁后自动调用
- (instancetype)init;
分配接收类的新实例,发送一条init消息,并返回已初始化的对象。
+ (Class)class;
+ (Class)superclass;
+ (BOOL)isSubclassOfClass:(Class)aClass;
返回一个布尔值,该值指示接收类是否为给定类的子类或与之相同。
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
判断一个实例变量是否能够调用一个类的方法
比如,
BOOL boolValue = [NSArray instancesRespondToSelector:@selector(initWithArray:)];返回YES;
BOOL boolValue = [NSArray instancesRespondToSelector:@selector(arrayWithArray:)];返回NO
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
判断一个接收者是否能够遵从一个给定的protocol。一个接收者遵从一个协议,可以是直接接受这个协议,也可以继承一个已经接受这个协议的类。
- (BOOL)isProxy;
判断一个实例不继承自NSObject,如果返回NO就是继承自NSObject,反之返回YES
- (IMP)methodForSelector:(SEL)aSelector;
定位并且返回接收者实现该方法的地址,因此可以作为一个方法来调用。
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
SEL只是方法编号,IMP是一个函数指针,保存了方法的地址
为什么不直接获得函数指针,而要从SEL这个编号走一圈再回到函数指针呢?
有了SEL这个中间过程,我们可以对一个编号和什么方法映射做些操作,也就是说我们可以一个SEL指向不同的函数指针,这样就可以完成一个方法名在不同时候执行不同的函数体
//方法编号
SEL methodId=@selector(func1);
//执行对应方法可以用respondsToSelector: 这个方法提前判断。
[selfperformSelector:methodIdwithObject:nil];
//通过编号获取方法名
NSString*methodName =NSStringFromSelector(methodId);
//IMP获得和使用
IMP methodPoint = [selfmethodForSelector:methodId];
methodPoint();
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
performSelector:withObject:函数可以直接调用这个消息。但是perform相关的这些函数,有一个局限性,其参数数量不能超过2个,否则要做很麻烦的处理,与之相对,NSInvocation也是一种消息调用的方法,并且它的参数没有限制。这两种直接调用对象消息的方法,在IOS4.0之后,大多被block结构所取代,只有在很老的兼容性系统中才会使用。
消息转发
方案一:
+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
void eat(id dd,SEL aa) {
NSLog(@"dog eat %@%s",dd,aa);
}
//处理未实现的方法,给sel添加方法实现
+(BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(eat)) {
class_addMethod(self, sel, (IMP)eat,"v@:");
return YES;
}
return [superresolveInstanceMethod:sel];
}
方案二:
- (id)forwardingTargetForSelector:(SEL)aSelector
//去cat对象找有没有eat方法
-( id )forwardingTargetForSelector:( SEL )aSelector {
return [[ Cat alloc ] init ];}
方案三:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
// 创建一个函数签名,这个签名可以是任意的,但需要注意,签名函数的参数数量要和调用的一致。
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(eat)) {
SEL sss = @selector(run:pram:pram:);
NSMethodSignature *sig = [CatinstanceMethodSignatureForSelector:sss];
return sig;
}
return [supermethodSignatureForSelector:aSelector];
}
//将消息转出某对象
-(void)forwardInvocation:(NSInvocation *)anInvocation {
if (anInvocation.selector ==@selector(eat)) {
Cat *cat = [[Catalloc]init];
SEL sss = @selector(run:pram:pram:);
NSString *a = @"a";
NSString *b = @"b";
NSString *c = @"c";
[anInvocation setTarget:cat];
[anInvocation setSelector:sss];
[anInvocation setArgument:&a atIndex:2];
[anInvocation setArgument:&b atIndex:3];
[anInvocation setArgument:&c atIndex:4];
if ([cat respondsToSelector:sss]) {
[anInvocation invoke];
}
}
}