由于某些原因,我不得不保留原始代码而不做任何修改,而是尝试将系统 API 重定向到我的代码中,然后再回调到原始代码。例如,我想在 [NSString stringWithFormat:] 中做更多的事情。
现在我尝试使用 method swizzling。但是似乎 NSString 在 main 运行时没有加载,我将 swizzling 方法移动到 MyAppDelegate。之后 class_getInstanceMethod([NSString class], @selector(stringWithFormat:)) 不为零。但是,swizzling 方法仍然无效,因为 class_getInstanceMethod([NSString class], @selector(override_stringWithFormat:)) 仍然是 nil,我该如何解决?
谢谢, 俱乐部
@interface NSString (MyNSString)
+ (id)stringWithFormat:(NSString *)format, ...;
@end
@implementation NSString (MyNSString)
+ (id)stringWithFormat:(NSString *)format, ... {
//do something...
[NSString stringWithFormat:format];
}
@end
这是MyAppDelegate中的代码
#import "MyNSString.h"
-(void) MethodSwizzle:(Class)c replaceOrig:(SEL) origSEL withNew:(SEL) overrideSEL {
Method origMethod = class_getInstanceMethod(c, origSEL);
Method overrideMethod = class_getInstanceMethod(c, overrideSEL);
if(class_addMethod(c, origSEL, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)))
class_replaceMethod(c, overrideSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, overrideMethod);
}
- (BOOL) application:(UIApplication*) application didFinishLaunchingWithOptions:(NSDictionary *) options {
...
//Unit test
NSString *a=[NSString override_stringWithFormat:@"Test"]; //returned something
Method b = class_getInstanceMethod([NSString class], @selector(override_stringWithFormat:)); //return nil;
//do something...
[self MethodSwizzle:[NSString class] replaceOrig:@selector(stringWithFormat:) withNew:@selector(override_stringWithFormat:)];
}
最佳答案
不确定您要做什么,问题有点不清楚。另外,不要试图从 NSString 继承,它是类簇的一部分,所以它比只覆盖一个方法要复杂得多。你甚至必须 mplement the storage yourself .变得非常复杂。
有两种方法可以在 Objective-C 中向现有类添加方法:Categories和 method Swizzling .
类别允许您在现有类上定义新方法,您不能可靠地使用类别替换方法。例如:
@interface NSString (MyStuff)
- (id)mySpecialInit;
@end;
@implementation NSString (MyStuff)
/* implementation of -(id)mySpecialInit would go here */
@end
同样,此方法不会可靠地替换类中的现有方法。
为此,您需要方法调配。警告 - 这是运行时的一项高级功能,很容易让自己陷入调试噩梦,因此请谨慎使用。 Swizzling 允许您用具有相同签名的另一种方法替换一种方法的实现。使用一些技巧,您也可以调用 super 实现。 Matt Gallagher 通常被认为拥有 best implementation of method swizzling ,因为它允许可靠地调用预先存在的实现(称为后续实现)。查看有关 Cocoa with Love 的链接文章,了解示例用法。您编辑的代码可能不起作用,因为您正在尝试调配类方法,但使用的是 getInstanceMethod 函数。请改用 class_getClassMethod() 。此外,您需要重命名您的类别 stringWithFormat 方法,它不得与真正的方法名称冲突。最后一个警告——我从未见过在带有可变参数 (...) 和 this C q&a 的方法上使用方法调配。让我相信这是不可能的(尝试 this post 作为可能的解决方案)。作为一项学术练习,下面是它可能的样子:
NSString+MyMethods.h
@interface NSString (MyMethods)
+ (id)clu_stringWithFormat:(NSString *)format,...;
@end
NSString+MyMethods.m:
@implementation NSString (MyMethods)
+ (id)clu_stringWithFormat:(NSString *)format,... {
//Do your stuff here
//call supersequent implementation
//again, I have no idea if this will work as is, you might need to tweak how it passes the variable argument list
invokeSupersequent(format);
}
+(void)load {
//do your method swizzle in here, this is called one time only
Method origMethod = class_getClassMethod([NSString class], @selector(stringWithFormat:);
Method replMethod = class_getClassMethod([NSString class], @selector(clu_stringWithFormat:);
method_exchangeImplementations(origMethod, replMethod);
}
鉴于这可能是不可能的,否则是一个非常糟糕的主意 - 您正在更改类集群中特定类的操作方式,Apple 表示“不要这样做”。并且有充分的理由 - 如果有人调用 [NSMutableString stringWithFormat:] 或分配一个 NSString 然后调用 -initWithFormat: 会发生什么?创建具有某种格式的字符串有很多有效的路径,你将有一段痛苦的时间试图维护拦截所有这些的代码。我鼓励您重新考虑它的设计——无论您试图解决什么问题,都可能有一种更简单、更易于维护的方法来实现它。例如,添加您自己的字符串工厂。
关于iphone - 是否可以在 Objective-C 中重定向系统 API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6462907/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下