iOS学习 - CocoaPods & 传值常用技术

502 阅读8分钟

iOS学习 - CocoaPods & 传值常用技术

  • 学习日期:2021 - 12 - 18
  • 署名:SSR

CocaoPods

介绍

  • 是什么: 管理iOS项目的依赖管理器

  • 使用的语言: Ruby

  • 目的: 自动安装/更新第三方依赖库

  • 为什么: 没有之前,只能手动往项目导入第三方库

    拷贝源码 => 添加framwork =>设置BuildPhase

  • 优点:

    • 中心化的配置: 所有依赖的第三方库都写在Podfile里,便于管理。
    • 便于更新: 中心化的配置,只需要Podfile中更新版本号
    • 一件集成: 只需执行pod install,就能自动安装所有依赖库
    • 自动处理依赖: CocoaPods会自动安装第三方依赖库

Ruby

  • 是什么: 一种简单快捷的面向对象的脚本语言

  • 扩展性: 可运行于多钟平台

  • 特点:

    • 完全面向对象: 任何东西都是对象,基本类型也是对象
    • 变量没有类型: 可以保存任何类型数据
    • 任何东西都有值: 数学、逻辑表达式、句子 都会有值
  • 使用工具: RVM(全名: Ruby Version Manager)

    • 命令行工具: 提供一个便捷的多版本Ruby环境的管理和切换
  • 依赖: Ruby Project 依赖于一系列的 Ruby Gems

    • 管理: 用Bundler为Ruby Project提供统一构建环境
  • 有关网址: baike.baidu.com/item/Ruby/1…

  • RubyGems(简称: Gems)

    • 作用: 可以轻松下载,安装和使用系统上的Ruby软件包

    • Ruby软件包(Gem)

      • 是什么: 是Ruby模块(Gems)的包管理

      • 构建: 依照.gemspec文件构建的

      • 包含: YAML文件应用程序和库

      • 功能: 与apt-getportageyumnpm相似

    • 依赖文件(Gemfile)

      • 是什么: 描述Gems之间依赖的文件
      • 定义: 从哪个 源 找到这些GemsGems的版本

CocoaPods 安装/更新

  • 问题分析

    为避免不同电脑的CocaPods版本不一样而导致的冲突

    Bundller 给出了更好的解决方案

  • 步骤

    • 创建一个Gemfile文件,输入:
    source 'https://rubygems.org' do
        gem 'cocoapods', '1.5.0'
    end
    
    • 执行bundle install

      bundler会根据Gemfile内容创建Gemfile.lock文件

      此文件要放入git等版本控制中,同时会下载Gemfile中指定的版本

    • 执行bundle exec pod install

      bundler会检查按需下载安装相应版本的CocoaPods并进行pod install操作

Podfile

zhuanlan.zhihu.com/p/45513672

  • 是什么: 是一个描述了项目的具体依赖关系文件(使用Ruby语言)
  • 编写Podfile文件:
platform :ios, '10.0' #基于iOS 10.0版本
#原型 platform(:ios, '10.0')
#:ios 这种语法在 Ruby 中叫符号(Symbol)。
#Ruby 区分了符号和字符串,符号内容不可变,字符串内容可变。
	#因为符号内容并不可变,假如它们内容相同,就是同一个对象,共用同一份内存;
	#但是字符串可变,就算它们内容相同,也是分离的两个对象。
	#在 Ruby 中经常使用符号,特别是作为字典的 key。

use_frameworks! 
#原型 use_frameworks!()
#Ruby 的函数参数可以有默认值。这里不传参数,flag 就默认为 true。
#Ruby 的名字可以带感叹号 !,表示强调,需要特别注意或者有某种危险。
#Ruby 的名字也可以带问号 ?,表示某种疑问。

#source '资源来源'
source 'https://github.com/CocoaPods/Specs.git'

#target '项目名称' do
# pod '库名'  //导入第三方库
# pod '库名', '~>版本号'  //规定导入版本号的第三方库
#end
#本质 构成了一个Block
target 'SSRApp' do
    pod 'Masonry'
    pod 'AFNetworking'

	#target "测试项目Tests" do
	target "SSRAppTests" do
		inherit! :search_paths
   		pod 'OCMock', '~>2.0.1'
	end
end


传值常用技术

传值一般有 属性block协议/代理消息通知传值

属性传值

  • 在被需要值的类的头文件中,将被需要的值暴露给外部使用

  • 优点: 方便快捷

  • 缺点: 容易被改写

    最好用readonly

block

  • 基础知识

    • 组成部分以及实现

      返回值类型 (^函数名) (函数参数) = (返回值) (^) (函数参数) {代码;};

    • 调用: 若有返回值,则用相应类型去接收

      函数名 (函数参数);

    • 注意的点:

      • 循环引用: 相互持有对方,造成两者无法释放,引起内存泄漏问题。

        解除方式: 将任意一方引用 (__weak) ,解除相互持有

      • 改变block捕获变量的值:

        使用__block

  • 作为参数

    • 使用场景: 网络请求等耗时操作

      ​ 需要对当得到一大串数时所使用操作

      ​ 用于一些信息或错误等处理

    • 使用例子:

      基于命令行,用户输入两个整数,写一个类方法去判断是不是质数,写一个类方法去遍历[a ,b]中的所有质数转交回去,实现将质数平方后输出SSR-block-质数的平方为:x,并且返回个数。

      错误反馈: 创建enum枚举出找不到区间后者为负数情况,并交回处理。

      //NSNumber+Prime.h
      #import <Foundation/Foundation.h>
      typedef NS_ENUM(NSInteger,PrimeInterval){
          PrimeIntervalNoInterval,//找不到区间(区间写反了)
          PrimeIntervalNegativeNumber//后者为负数
      };
      @interface NSNumber(Prime)
      + (BOOL)isPrime:(int)number;
      + (int)countFrom:(int)begin to:(int)end 
          seccess:(void)(^primeSeccess)(int prime)
          error:(void)(^primeError)(PrimeInterval error);
      @end
      
      //NSNumber+Prime.m
      @impletmentation NSNumber(Prime)
      + (BOOL)isPrime:(int)number{
          if(number <= 1)
              return NO;
          for(int i = 2; i*i <= number; i++)
              if(number % i == 0)
              	return NO;
          return YES;
      }
      + (int)countFrom:(int)begin to:(int)end 
          seccess:(void)(^primeSeccess)(int prime)
          error:(void)(^primeError)(PrimeInterval error){
          if(end <= begin){
              primeError(PrimeIntervalNoInterval);
              return 0;
          }
          if(end <= 0){
              primeError(PrimeIntervalNoInterval);
          }
          int sum = 0;
          for(int i = begin; i <= end; i++)
              if([NSNumber isPrime:i]){
              	sum++;
              	primeSeccess(i);
          }
          return sum;
      }
      @end
      
      //main.c
      #import <Foundation/Foundation.h>
      #import "NSNumber+Prime.h"
      int main()
      {
          void (^success)(int prime) = ^(int prime){
              NSLog(@"SSR-block-质数的平方为:%ld", prime * prime);
          }
          void (^error)(PrimeInterval errorNum){
              switch(PrimeInterval){
                  case PrimeIntervalNoInterval:
                      NSLog(@"无此区间/区间写反了");
                      break;
                  case PrimeIntervalNegativeNumber
                      NSLog(@"后者为负数,不存在质数");
                      break;
                  default:
                      NSLog(@"未知错误!!!");
              }
          }
          
          //测试1:输入 较小正数 较大正数
          NSLog(@"测试1:从23到100")
          int sum1 = [NSNumber countFrom:23 to:100 
          			seccess:seccess
         				error:error];
          NSLog(@"总数为:%d",sum1);
          
          //测试2:输入 较大正数 较小正数
          NSLog(@"测试2:从100到23")
          int sum2 = [NSNumber countFrom:100 to:23
          			seccess:seccess
         				error:error];
          NSLog(@"总数为:%d",sum2);
          
          //测试3:输入 负数 1
          NSLog(@"测试3:从-23到1")
          int sum3 = [NSNumber countFrom:-23 to:1
          			seccess:seccess
         				error:error];
          NSLog(@"总数为:%d",sum3);
          
          //测试4:输入 负数 较大正数
          NSLog(@"测试1:从-23到100")
          int sum4 = [NSNumber countFrom:-23 to:100 
          			seccess:seccess
         				error:error];
          NSLog(@"总数为:%d",sum4);
          
          //测试5:输入 正数 负数
          NSLog(@"测试5:从23到-23")
          int sum5 = [NSNumber countFrom:23 to:-23 
          			seccess:seccess
         				error:error];
          NSLog(@"总数为:%d",sum5);
          return 0;
      }
      
  • block作为属性

    • 当其他类访问时,可以直接通过属性出来

      优点: 方便快捷

      缺点: 暴露在外面

    • 使用例子

      SSR商品有商品名字,单价,数量,股值(单价*数量)

      当用户买了过后,股值发生变化,数量相应减少

      当没有货物时,应告诉用户你还差购买的商品数

      //SSRCommodity.h
      #import <Foundation/Foundation.h>
      @interface SSRCommodity:NSObject
      @property (nonatomic, copy, readonly) float totalPrices;
      + (instancetype) initWithName:(NSString *)name 
          price:(float)price amount:(NSUInterger)amount;
      - (NSUInterger)amountAfterSelledWithAmount:(NSUInterger)amount 
          success:(void(^)(NSUInterger make)makemoney)
          errorEmpity:(void(^)(NSUInterger remain)empity);
      @end
      
      //SSRCommodity.m
      #import "SSRCommodity.h"
      @interface SSRCommodity ()
      @property (nonatomic, strong) NSString *name;
      @property (nonatomic) float price;
      @property (nonatomic) NSUInteger amount;
      @end
      
      @implementation SSRCommodity
      + (instancetype) initWithName:(NSString *)name 
          price:(float)price amount:(NSUInteger)amount{
          if(self = [super init]){
              _name = name;
              _price = price;
              _amount = amount;
              _totalPrices = price * amount;
          }
          return self;
      }
      - (NSUInterger)amountAfterSelledWithAmount:(NSUInterger)amount 
          success:(void(^)(NSUInterger make)makemoney)
          errorEmpity:(void(^)(NSUInterger remain)empity){
          if(amount <= self.amount){
              _amount -= amount;
              _totalPrices = price * amount;
              makemoney(amount * price);
          }
          else{
              amount -= _amount;
              _amount = 0;
              _totalPrices = 0;
              empity(amount);
          }
          return _amount;
      }
      @end
      
      //main.h
      #import <Foundation/Foundation.h>
      #import "SSRCommodity.h"
      int main()
      {
          SSRCommodity *shoes = [SSRCommodity 
                                 initWithName:@"Adidas" price:2999 amount: 366];
          NSLog(@"股值:%ld",shoes.totalPrices);
      
          NSLog(@"Day-1:卖出:200件,记录如下:");
          NSUInterger made1 = [shoes amountAfterSelledWithAmountt:200
                        success:((^)(NSUInterger make){
                            NSLog(@"\t赚得:%ld",make);
                        })
                        errorEmpity:(void(^)(NSUInterger remain)empity){
                            NSLog(@"\t卖空了,你还差%ld件未买到", empity);
                        }];
          NSLog(@"\t仓库还剩数量:%ld\n\t股值:%ld", made1, shoes.totalPrices);
      
          NSLog(@"Day-2:卖出:300件,记录如下:");
          NSUInterger made2 = [shoes amountAfterSelledWithAmount:300
                        success:((^)(NSUInterger make){
                            NSLog("赚得:%ld",make);
                        })
                        errorEmpity:(void(^)(NSUInterger remain)empity){
                            NSLog("卖空了,你还差%ld件未买到", empity);
                        }];
          NSLog(@"\t仓库还剩数量:%ld\n\t股值:%ld", made2, shoes.totalPrices);
          return 0;
      }
      

通知中心

  • 是什么: 由发送者1或者多个发送者n,

    ​ 通过发送一个通知到通知中心,

      	    接收者1或者多个接收者都可以接收到发送过来的通知
    
  • 使用场景

    • 两个毫无关系的类之间进行交互操作
    • 对于一些必要的,对于系统活动的监听(如键盘弹起、收回时机)
  • 属性

    • 这个消息的名字(唯一标识),用于辨别消息对象。(可以看成是消息频道)

      @property (readonly, copy) NSNotificationName name;
      
    • 发送的对象,即这个消息只发送给某一个对象

      @property (nullable, readonly, retain) id object;
      
    • 字典,作用是传递各种各样的值

      @property (nullable, readonly, copy) NSDictionary *userInfo;
      
  • 创建

    • 通知中心是单例

      每一个应用程序会有一个默认的通知中心。用于调度通知发送和接受。

    • 创建代码

      NSNotificationCenter *ncc = [NSNotificationCenter defaultCenter];
      

      注意: 不能使用init进行初始化

  • 注册通知

    • 作用: 可以为观察者指定一个方法,名字,对象。接受到通知时,便执行方法。

    • 代码定义

      - (void) addObserver:(id)observer 
      		 selector:(SEL)aSelector
      		 name:(NSString *)aName 
      		 object:(id)anObject
      
    • 参数介绍

      • (id)observer:观察者(注册者)
      • (SEL)aSelector:当观察者接受到通知时执行的方法
      • (NSString *)aName:通知的名字
      • (id)anObject:限定被通知的对象
  • 发送通知

    • 代码定义

      - (void) postNotification:(NSNotification *)notification;
      - (void) postNotificationName:(NSString *)aName Object:(id)anObject;
      - (void) postNotificationName:(NSstirng *)aName object:(id)anObject
               userInfo:(NSDictionary *)aUserInfo;
      
    • 参数介绍

      • (NSNotification *)notification:通知对象(本质是第三个代码参数的对象化)
      • (NSString *)aName: 消息名字(频道)
      • (id)anObject: 接受固定对象的通知,nil时接受所有对象发送该消息名的消息
      • (NSDictionary *)aUserInfo: 发送消息时传递的各种值
  • 移除通知

    • 代码定义

      - (void)removeObserver:(id)observer;
      - (void)removeObserver:(id)observer 
          	name:(NSString *)aName object:(id)anObject
      
    • 介绍

      • removeObserver:直接移除掉这个观察者,这个观察者上注册的所有通知都作废
      • removeObserver:name:object:只移除这个观察者某个通知
  • 注意点

    • 通知中心注册时的方法((SEL)aSelector)必须要实现,不实现会崩溃

    • 书写通知中心代码

      • viewWillApperar中注册,viewWillDisappear中移除,减少对于性能的影响
      • viewDidLoad中注册。如果在viewWillApperar中注册却没有在viewWillDisappear中移除那么会造成注册很多次,可能会造成一个事件触发后被多次响应。
    • 通知中心很强大,但对性能的影响代码的整洁性协作开发来说,通知中心永远作为备选。