1. 定义一个协议, 一个协议可以扩展子另一个协议 如果需要扩展多个协议中间使用逗号分隔

     //定义一个协议
    @protocol AnimalDelegate <NSObject, ***> @required //必须实现的方法
    -(void)eat; @optional //可选实现的方法
    -(void)run;
    -(void)say;
    -(void)sleep;
    @end
  2. 和其他高级语言中接口不同的是协议中定义的方法不一定是必须实现的。 我们可以通过关键字进行@required和@optional进行设置,如果不设置则默认是@required(注意ObjC是弱语法,即使不实现必选方法编译运行也不会报错);

  3. 协议通过<>进行实现,一个类可以同时实现多个协议,中间通过逗号分隔;

  4. 协议的实现只能在类的声明上,不能放到类的实现上(也就是说必须写成@interface Person:NSObject而不能写成@implementation Person);

  5. 协议中不能定义属性、成员变量等,只能定义方法;

     @protocol CAMediaTiming
    
     /* The begin time of the object, in relation to its parent object, if
    * applicable. Defaults to 0. */ @property CFTimeInterval beginTime; /* The basic duration of the object. Defaults to 0. */ @property CFTimeInterval duration; /* The rate of the layer. Used to scale parent time to local time, e.g.
    * if rate is 2, local time progresses twice as fast as parent time.
    * Defaults to 1. */ @property float speed; /* Additional offset in active local time. i.e. to convert from parent
    * time tp to active local time t: t = (tp - begin) * speed + offset.
    * One use of this is to "pause" a layer by setting `speed' to zero and
    * `offset' to a suitable value. Defaults to 0. */ @property CFTimeInterval timeOffset; /* The repeat count of the object. May be fractional. Defaults to 0. */ @property float repeatCount; /* The repeat duration of the object. Defaults to 0. */ @property CFTimeInterval repeatDuration; /* When true, the object plays backwards after playing forwards. Defaults
    * to NO. */ @property BOOL autoreverses; /* Defines how the timed object behaves outside its active duration.
    * Local time may be clamped to either end of the active duration, or
    * the element may be removed from the presentation. The legal values
    * are `backwards', `forwards', `both' and `removed'. Defaults to
    * `removed'. */ @property(copy) NSString *fillMode; @end

    上面的协议是苹果的原生代码,但你会看到协议里面有很多@propety,那这个不就和上面的协议中不能定义属性、成员变量等,只能定义方法;有点相违背么?这就会产生以下几个问题:

    1. @propety是什么,干了什么? 对于.m 文件中的这个 @synthesize有什么作用?

       @property与@synthesize是成对出现的,可以自动生成某个类成员变量的存取方法。在Xcode4.5以及以后的版本,@synthesize可以省略。编译器会自动帮你加上getter 和 setter 方法的实现,并且默认会去访问_XXX这个成员变量,如果找不到_XXX这个成员变量,会自动生成一个叫做 _XXX的私有成员变量。
      ---
      @interface Student : NSObject
      {
      int age;
      int no;
      } //当编译器遇到@property时,会自动展开成getter和setter的声明
      @property int age;
      @property int no;
      @end ---- @implementation Student
      //@synthesize 会自动生成getter和setter的实现
      //@synthesize 默认会去访问age,no,height同名的变量,,
      //如果找不到同名的变量,会在内部自动生成一个私有同名变量age,no
      //因此Student.h 中的这几个变量也可以省略不写。 @synthesize age,no; // @synthesize age = _age, no = _no; // 生成两个私有变量 _age, _no
      @end
    2. 如果我就自己也写一个这样的协议,然后使用它会不会有问题?

       self.fillModel = @"test"; // unrecognized selector
      
       //这只是在头文件中声明,编译器是不会自动生成实例变量的,也就是说你可以用一个对象去实现这个协议,但是你不自己存储的话实例变量的话,访问同样会崩溃.
      
       // 如何去避免这种崩溃呢,下面的例子是一种方案,同时也证明了@synthesize的作用
      
       @protocol ViewContDelegate <NSObject>
      @property float repeatCount;
      @end @interface ViewController ()<ViewContDelegate>
      @end @implementation ViewController
      @synthesize repeatCount = _repeatCount; - (void)viewDidLoad {
      [super viewDidLoad];
      self.repeatCount = 1;
      NSLog(@"%f -- %f", self.repeatCount, _repeatCount);
      } @end
    3. 类中的声明@propety 和 协议中声明@propety 有区别么?

       两则没有区别,都是申明了setter/getter. 而在类中的.m 在Xcode4.5以后版本@synthesize可以被忽略。而生成的变量的操作其实还是由@synthesize完成的。
    4. 对于category也有类似的规范,那么为什么使用关联对象可以对类添加属性,category实现的原理?

       1. 这个技术并不是category的特性,而是runtime.h的一种应用。iOS运行时机制,简单来说,就是苹果给开发这提供的一套在运行时动态创建类、添加属性/方法.
      OC把实例变量当做一种存储偏移量所用的"特殊变量",交由"类对象"保管。偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也就变了,这样的话,无论何时访问实例变量,总能使用正确的偏移量。甚至可以在运行期间向类中新增实例变量,这就是ABI,有了这种稳固的ABI,我们就可以在“class-continuation 分类”或者实现文件中定义实例变量了。所以说,不一定要在接口中把全部的实例变量都声明好,可以将某些变量从接口的public区段移走,以便保护与类实现有关的内部信息 2. 有没有主动引入 Category 的头文件,Category 中的方法都会被添加进主类中。我们可以通过 - performSelector: 等方式对 Category 中的相应方法进行调用,之所以需要在调用的地方引入 Category 的头文件,只是为了“照顾”编译器同学的感受。 在文件 objc-runtime-new.mm 中找到以下函数:
      void _read_images(header_info **hList, uint32_t hCount){}在这个函数中对 Category 做了如下处理: 1. 将 Category 和它的主类(或元类)注册到哈希表中;
      2. 如果主类(或元类)已实现,那么重建它的方法列表。 Category 中的实例方法和属性被整合到主类中;而类方法则被整合到元类中。对协议的处理比较特殊,Category 中的协议被同时整合到了主类和元类中。 不管是哪种情况,最终都是通过调用 static void remethodizeClass(Class cls) 函数来重新整理类的数据的。这个函数的主要作用是将 Category 中的方法、属性和协议整合到类(主类或元类)中,更新类的数据字段 data() 中 method_lists(或 method_list) 、 properties 和 protocols 的值。进一步,我们通过 attachCategoryMethods 函数的源码可以找到真正处理 Category 方法的 attachMethodLists 函数:主要作用就是将类中的旧有方法和 Category 中新添加的方法整合成一个新的方法列表,并赋值给 method_lists 或 method_list 。通过探究这个处理过程,我们也印证了一个结论,那就是主类中的方法和 Category 中的方法在 runtime 看来并没有区别,它们是被同等对待的,都保存在主类的方法列表中。不过,类的方法列表字段有一点特殊,它的结构是联合体, method_lists 和 method_list 共用同一块内存地址。当 newCount 的个数大于 1 时,使用 method_lists 来保存 newLists ,并将方法列表的 标志位 置为 RW_METHOD_ARRAY ,此时类的方法列表字段是 method_list_t 类型的指针数组;否则,使用 method_list 来保存 newLists ,并将方法列表的 标志位 置空,此时类的方法列表字段是 method_list_t 类型的指针。 runtime 对 Category 中方法的处理过程并没有对 +load 方法进行什么特殊地处理。因此,严格意义上讲 Category 中的 +load 方法跟普通方法一样也会对主类中的 +load 方法造成覆盖,只不过 runtime 在自动调用主类和 Category 中的 +load 方法时是直接使用各自方法的指针进行调用的。所以才会使我们觉得主类和 Category 中的 +load 方法好像互不影响一样。因此,当我们手动给主类发送 +load 消息时,调用的一直会是分类中的 +load 方法
  6. 事实上在ObjC中协议的更多作用是用于约束一个类必须实现某些方法,而从面向对象的角度而言这个类跟接口并不一定存在某种自然关系,可能是两个完全不同意义上的事物,这种模式我们称之为代理模式(Delegation)。在Cocoa框架中大量采用这种模式实现数据和UI的分离,而且基本上所有的协议都是以Delegate结尾。

文章中的引用:

  1. 深入理解Objective-C:Category
  2. iOS开发系列--Objective-C之协议、代码块、分类
  3. Objective-C Category 的实现原理

iOS 之 protocol的相关问题的更多相关文章

  1. 转:基于IOS上MDM技术相关资料整理及汇总

    一.MDM相关知识: MDM (Mobile Device Management ),即移动设备管理.在21世纪的今天,数据是企业宝贵的资产,安全问题更是重中之重,在移动互联网时代,员工个人的设备接入 ...

  2. 基于IOS上MDM技术相关资料整理及汇总

    (转自:http://www.mbaike.net/special/1542.html) 一.MDM相关知识:MDM (Mobile Device Management ),即移动设备管理.在21世纪 ...

  3. iOS音乐播放器相关

    iOS音乐播放器框架主要有两大类:AvPlayer.AvaudioPlayer AvPlayer 能播放本地及网络歌曲 AvaudioPlayer 能播放本地歌曲.有相关代理方法(其实也可以播放网络歌 ...

  4. iOS 10 开发 相机相关的适配

    升级 iOS 10 之后目测坑还是挺多的,记录一下吧,看看到时候会不会成为一个系列. 直入正题吧 今天在写 Swift 3 相关的一个项目小小练下手,发现调用相机,崩了.试试看调用相册,又特么崩了.然 ...

  5. iOS UIViewController 和 nib 相关的3个方法

    iOS UIViewController 的 awakeFromNib 以及 - (id)initWithCoder:(NSCoder *)aDecoder 和 - (instancetype)ini ...

  6. iOS,文本输入,键盘相关

    1.UIKeyboard键盘相关知识点 2.点击空白区域隐藏键盘(UIKeyboard) 3.键盘(UIKeyboard)挡住输入框处理 4.自定义键盘(UIKeyboard) 5.监听键盘弹出或消失 ...

  7. iOS之即时通讯相关理解

    Socket: 1>Socket又称"套接字" 2>网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. 3>应用程序通常通 ...

  8. iOS应用程序状态切换相关

    一.iOS应用程序状态机一共有五种状态: 1. Not running:应用还没有启动,或者应用正在运行但是途中被系统停止. 2. Inactive:当前应用正在前台运行,但是并不接收事件(当前或许正 ...

  9. ios开发-获取手机相关信息

    今天在做客户端的时候,里面有个意见反馈功能. 调用系统带的邮件功能,发送邮件到指定邮箱. 然后我就想,应该在邮件正文部分添加手机相关内容,比如型号,版本,应用程序的版本等等,这样不仅使用者方便,开发者 ...

随机推荐

  1. JavaScript配合button.onclick()使用总结

    Html加载顺序是从上往下加载,如果script中含有引用js脚本,则应该将此script放在head标签里面,这样可是保证此页面都可以引用js脚本内容.如果想在script中设置button.onc ...

  2. 《Java从入门到放弃》JavaSE入门篇:练习——单身狗租赁系统

    今天,我们要玩个大的!!! 我们把之前使用数组做的这个单身狗系统改版成数据库版本,并且使用面向对象里面的一些简单思想.如果有不知道这个系统的看官,请跳转到目录页,然后再选择单身狗系统(数组版)先围观五 ...

  3. Elipse中发布一个Maven项目到Tomcat

    对于maven初学者的我,经常遇到一个问题就是,maven项目创建成功后,本来已经添加了jar的依赖,但是发布到Tomcat中就是没有jar包存在, 启动Tomcat总是报没有找到jar包,可项目结构 ...

  4. Flask-WTF 入门使用P1

    创建表单 Flask-WTF为您的Flask应用程序集成了WTForms,具体例子如下: from flask_wtf import FlaskForm from wtforms import Str ...

  5. Linux 更改ssh 端口

    部署了一个测试服务器之后,在查看linux日志的时候,发现莫名的IP一直在访问服务器,感觉像是某种恶意扫描,来攻击服务器的.因此更改ssh端口. 输入: vim /etc/ssh/sshd_confi ...

  6. FoxOne---一个快速高效的BS框架--数据访问(Dao)

    FoxOne---一个快速高效的BS框架--(1) FoxOne---一个快速高效的BS框架--(2) FoxOne---一个快速高效的BS框架--(3) FoxOne---一个快速高效的BS框架-- ...

  7. TomCat系统架构

    1.TomCat总体结构 TomCat有两大核心组件:Connector和Container.Connector组件是可以被替换的,一个Container可以对应多个Connector. 多个Conn ...

  8. Linux入门之常用命令(12) mount

    查看Linux所有设备 cd  /dev ls -l sd*  //分区 查看Linux磁盘 fdisk -lu 挂载 一般挂载至/mnt /media mount /dev/sda5   /mnt/ ...

  9. JSP获取input(含正则表达式)

    <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> < ...

  10. 在Pycharm中使用jupyter笔记本

    在Pycharm中使用jupyter笔记本 我在Pycharm中使用jupyter笔记本,发现新的Jupyter更新中,增加了令牌. 随着创建的虚拟环境启动的所有设置,并将URL设置为127.0.0. ...