最近项目时间比较紧,加上最近工作也有点忙,结果blog又开始长草了-,-。。。现在分享一下记下最近cocoa 开发时近遇到的一个小问题
遇到的问题
Protocol和Delegate是cocoa里面比较常用的设计模式,多用在对象之间进行数据交互,但是最近在我的项目中,发现,使用self.xxxdelegate = xxxdelegate
时运行没有任何问题,但是在编译时会出现类型不匹配的警告
like this ↓
大家不用怀疑Obj1里面我肯定是实现了Obj1Delegate的,那为啥这里赋值的时候,却说类型不匹配呢,挺奇怪的啊。
问题分析
编译器肯定不会忽悠我们的,那肯定还是代码写的有问题,我们来看下代码吧(偷下懒,写在一起了)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| @protocol Obj1Delegate <NSObject>
- (void)dosth;
@end
@interface Obj1 : NSObject
@property (weak) id<Obj1Delegate> delegate; - (void)foo;
@end
#import "Obj1.h"
@implementation Obj1
- (void)foo { [self.delegate dosth]; }
@end
#import <Foundation/Foundation.h>
@interface Obj2 : NSObject
@end
#import "Obj2.h" #import "Obj1.h"
@interface Obj2 () <Obj1Delegate>
@end
@implementation Obj2
- (void)dosth { NSLog(@"dosth"); }
@end
#import "AppDelegate.h" #import "Obj1.h" #import "Obj2.h"
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { Obj1 *o1 = [[Obj1 alloc] init]; Obj2 *o2 = [[Obj2 alloc] init]; o1.delegate = o2; }
@end
|
很多童鞋肯定觉得,目测没问题啊。嗯嗯,确实是目测没问题,那么我们来想下为啥编译器会说不匹配呢?从编译器说不匹配的表象上来看,是编译器不知道Obj2实现了Obj1Delegate接口,为啥不知道呢。。。那就是我们的代码没有告诉他,那我们再来看关键的那部分代码,Obj2怎么实现的Obj1Delegate,以及我们怎么使用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #import "Obj2.h" #import "Obj1.h"
@interface Obj2 () <Obj1Delegate> //在.m文件中实现了接口
@end
@implementation Obj2
- (void)dosth { NSLog(@"dosth"); }
@end
#import "AppDelegate.h" #import "Obj1.h" #import "Obj2.h"
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { Obj1 *o1 = [[Obj1 alloc] init]; Obj2 *o2 = [[Obj2 alloc] init]; o1.delegate = o2; }
@end
|
感觉似乎找到问题了,我们在.m文件里面实现了接口,但是我们import的时候肯定是import的.h文件,而.h文件里面没有描述它实现了这个接口,所以,编译器在编译的时候就无法知道.m里面已经实现了,所以在这里他有一个警告,那我们对代码稍作修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #import "Obj1.h"
@interface Obj2 : NSObject <Obj1Delegate> //实现的申明放到了这里
@end
#import "Obj2.h"
@interface Obj2 ()
@end
@implementation Obj2
- (void)dosth { NSLog(@"dosth"); }
@end
|
这样编译就不会有警告了,至此,问题解决了,但是有的童鞋又觉得奇怪了,那为啥一开始的时候编译器都不知道有这个,那么为啥程序还能正常run呢。
后面的疑问
为啥能正常run,这个应该是C语言编译和连接的问题了,目测Objc上也是这样的。看下面的程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int main() { int a = foo(); printf("%d\n", a);
return 0; }
int foo() { return 8; }
|
上面的代码,用gcc分别编译,之后连接,能够正常运行,并在屏幕上打印8,放到ide里面肯定警告曰:foo未找到之类的,但是却能正常run,这是为啥呢。C在编译和链接的时候,函数是在链接时才知道地址的,如果编译main.c的时候没有把foo.c编译得到的目标文件传递给gcc的话,链接器就会告诉我们找不到foo这个函数了。所以上面Objc也应该是类似的处理方式,在链接时,链接器才回去找obj2里面有没有dosht这个方法。
上面C语言的方式这个方式不是给大家开方便之门-,-在头文件中声明函数原型是非常好的习惯,这样可以让编译器帮你检查函数调用有没与问题,传入的参数是否正确等。
最后欢迎大家订阅我的微信公众号 Little Code
- 公众号主要发一些开发相关的技术文章
- 谈谈自己对技术的理解,经验
- 也许会谈谈人生的感悟
- 本人不是很高产,但是力求保证质量和原创