在上一篇我们了解了什么是Runtime的关联,接下来我们就给类目添加属性。
1. 给NSObject类动态添加属性
头文件定义部分
@interface UIWebView (LoadProgress)
@property (nonatomic, assign) NSInteger resourceCount;
@end
实现部分
首先要定义一个全局的key
// resourceCount object keys
static void *s_resourceCountKey = &s_resourceCountKey;
//static void *s_resourceCountKey = "s_resourceCountKey";
@implementation UIWebView (LoadProgress)
@dynamic resourceCount;
#pragma mark Accessors and mutators
- (NSInteger)resourceCount
{
NSNumber *resourceCountNumber = objc_getAssociatedObject(self, s_resourceCountKey);
if (! resourceCountNumber)
{
return 0;
}
else
{
return [resourceCountNumber integerValue];
}
}
- (void)setResourceCount:(NSInteger)rCount
{
objc_setAssociatedObject(self, s_resourceCountKey, [NSNumber numberWithInteger:rCount], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
...
这样就可以直接使用了
webView.resourceCount = 10;
问:使用runtime Associatedealloc方法关联的对象,需要在对象deadlloc的时候释放吗?
答:不管是在MRC还是在ARC下,均不需要手动去释放。根据WWDC 2011, Session 322 中发布的内存销毁时间表,被关联的对象在生命周期内要比对象本身释放的晚很多。它们会在NSObject的deadlloc方法中调用object_dispose( )方法中释放。
//对象的内存销毁时间表
1. 调用-release :引用计数变为0
* 对象正在被销毁,生命周期即将结束
* 不能再有新的 __weak 弱引用,否则将指向nil
* 调用 [self dealloc]
2. -dealloc
* 继承关系中最底层的子类再调用-dealloc
* 如果是MRC代码,则会手动释放实例变量 iVars
* 继承关系中每一层的父类都在调用-dealloc
3. NSObject 调用 -dealloc
* 只做一件事:调用Objective-C runtime 中的 object_dispose() 方法
4. 调用object_dispose()
* 为C++ 的实例变量们iVars 调用iVarsdestructors
* 为ARC状态下的实例变量们 iVars调用-release
* 解除所有使用runtime Associate方法关联的对象
* 解除所有__weak 引用
* 调用free()
2. 替换或变更NSObject类方法Method
Runtime不仅可以关联属性,还可以替换已有方法、添加新的方法等,基本替换方法:
// 替换类方法
// frome: CoconutKit
IMP HLSSwizzleClassSelector(Class clazz, SEL selector, IMP newImplementation)
{
// Get the original implementation we are replacing
Class metaClass = objc_getMetaClass(class_getName(clazz));
Method method = class_getClassMethod(metaClass, selector);
IMP origImp = method_getImplementation(method);
if (! origImp) {
return NULL;
}
class_replaceMethod(metaClass, selector, newImplementation, method_getTypeEncoding(method));
return origImp;
}
// 替换实例方法
IMP HLSSwizzleSelector(Class clazz, SEL selector, IMP newImplementation)
{
// Get the original implementation we are replacing
Method method = class_getInstanceMethod(clazz, selector);
IMP origImp = method_getImplementation(method);
if (! origImp) {
return NULL;
}
class_replaceMethod(clazz, selector, newImplementation, method_getTypeEncoding(method));
return origImp;
}
新方法定义
// Original implementation of the methods we swizzle
static id (*s_UIWebView__identifierForInitialRequest_Imp)(id, SEL, id, id, id) = NULL;
// Swizzled method implementations
static id swizzled_UIWebView__identifierForInitialRequest_Imp(UIWebView *self, SEL _cmd, id webView, id initialRequest, id dataSource);
方法初始化需要写在类方法+ (void)load中
+ (void)load
{
s_UIWebView__identifierForInitialRequest_Imp = (id (*)(id, SEL, id, id, id))HLSSwizzleSelector(self, @selector(webView:identifierForInitialRequest:fromDataSource:), (IMP)swizzled_UIWebView__identifierForInitialRequest_Imp);
}
实现部分
#pragma mark Swizzled method implementations
static id swizzled_UIWebView__identifierForInitialRequest_Imp(UIWebView *self, SEL _cmd, id webView, id initialRequest, id dataSource)
{
// 调用原方法
(*s_UIWebView__identifierForInitialRequest_Imp)(self, _cmd, webView, initialRequest, dataSource);
[self setResourceCount:self.resourceCount+1];
return [NSNumber numberWithInteger:self.resourceCount];
}
3. 代理方法检索
可直接定义到NSObject的Category中
// [self implementsProtocol:@protocol(UIActionSheetDelegate)]
// frome: CoconutKit
- (BOOL)implementsProtocol:(Protocol *)protocol
{
// Only interested in optional methods. Required methods are checked at compilation time
unsigned int numberOfMethods = 0;
struct objc_method_description *methodDescriptions = protocol_copyMethodDescriptionList(protocol, NO /* optional only */, YES, &numberOfMethods);
for (unsigned int i = 0; i < numberOfMethods; ++i) {
struct objc_method_description methodDescription = methodDescriptions[i];
SEL selector = methodDescription.name;
if (! class_getInstanceMethod([self class], selector)) {
NSString *selectorString = [NSString stringWithCString:sel_getName(selector) encoding:NSUTF8StringEncoding];
NSString *protocolName = [NSString stringWithCString:protocol_getName(protocol) encoding:NSUTF8StringEncoding];
HLSLoggerInfo(@"Class %@ does not implement method %@ of protocol %@", [self className], selectorString, protocolName);
selectorString = nil; // Just to remove unused variable warnings
protocolName = nil;
return NO;
}
}
return YES;
}
网友评论