不同的编程思想

一、面向对象编程(OOP)

简述

面向对象的特点就是:封装、多态、继承。通过封装数据和其他方法,面向对象的编程使软件开发更加以人为中心,符合人类的直觉。在面向对象编程世界里,一切皆为对象,对象是程序的基本单元,对象把程序与数据封装起来提供对外访问的能力,提高软件的重用性,灵活性和扩展性

继承与多态都是后来不断发展的过程中加入进去的。从某种意义上说,多态性是继承性的泛化,因为并不是原始方法或对象的所有属性都需要传输到新实体。相反,还可以选择重写一些属性。

与此同时,继承性和多态性并不是面向对象编程所特有的。真正的区别在于封装数据及其包含的方法。

【扩展】:面向对象编程的基本原则「开闭原则」具有两个主要特征:

(1)对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。

2)对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。

问题

面向对象变成是我们非常容易理解的编程方式,继承和多态更丰富了其内容,但是随之而来也造成了不少的困扰,面向对象编程的问题大多都是由此而来的。

  1. 香蕉猴子丛林问题

    这个问题可以简述为:这个类可能是另一个类的子类,因此你需要将它的父类也包含在内。然后你会发现,这个父类可能也是另一个类的子类,以此类推,最后要面对一堆代码。由此产生大量的多余内容也会造成性能问题,特别是在系统规模变得越来越大时,问题也越发严重。
  2. 脆弱的基类

    你的子类越多,继承的越多,你的基类越难以修改。
  3. 菱形继承问题

    利用继承可以将一类中的属性传递给其他类,但是你无法混合两个不同类的属性,起码常规方法是做不到的。
  4. 层级问题

    类上堆积的属性越多,建立适当的层次结构就越困难。在你所处理的属性集群中,可能A类共享了B类的一些属性,但不是全部属性,反之亦然。在大型复杂项目中,层次结构的问题会导致很大的混乱。
    对于层级问题,我们可能会想到进行没有层次结构的面向对象编程。我们可以使用属性集群,并根据需要继承、扩展或重写属性。也许这有点混乱,但的确可以解决一些问题。
    但是这里还存在一个问题:封装的全部目的是使数据片段彼此之间保持安全,从而使计算效率更高,但没有严格的层次结构,这是行不通的。
  5. 引用问题

    引用问题更像是香蕉猴子问题的延伸,当你只需要一个香蕉,但是却拿到了整片森林时,这片森林已经不再安全了,封装已经被破坏,你可以任意处置其中的数据。
  6. 类库庞大

    当系统越来越庞大时,我们的类库越来越多,且单个库也会越来越大,越来越不易阅读和掌握,潜在隐患增多,我们无法保证类库中的每个类在各种环境中百分之百的正确。

二、面向协议编程(POP)

简述

简单来说,协议就是一张代码实现蓝图,我们可以在这张蓝图上勾勒出可能需要实现的方法、属性和其他满足特定任务的功能模块。而类、结构或枚举都可以通过这张蓝图(协议)来提供对这些需求的实际实现。而任何满足协议要求的类型都被认为符合该协议,都需要实现该协议规定必须实现的方法和属性。

其实我们使用面向协议编程的方式,很大程度上就是为了弥补上述面向对象编程中遇到的问题。

抽象一点,协议仅仅是实现多态的代码的一种方式,其他很多方式也都可以实现:继承、泛型、值、函数等等。值是最简单的方法;函数比值的方式复杂一点,但是仍然很简单;泛型(无约束)也比协议简单。但完整地说,协议通常比类的继承结构要简单。

这里有一点启发性的建议:仔细考虑你的协议是塑造数据模型还是行为模型。对于数据,结构类型可能更容易。对于复杂的行为(例如具有多个方法的委托),协议通常更容易。(标准库中的集合协议有点特殊:它们并不真的描述数据,而是在描述数据处理。)

也就是说,尽管协议非常有用,但是不要为了面向协议而使用协议。首先检视你的问题,并且尽可能地尝试用最简单的方法解决。通过问题顺藤摸瓜找到解决办法,不要背道而驰。面向协议编程并没有好坏之分,跟其他的技术(函数编程、面向对象、依赖注入、类的继承)一样,它能解决一些问题,但我们不应盲目,要择其善者而从之。有时候使用协议,但通常还有更简单的方法。

参考【为什么我们应该批评使用协议】

关于面向协议编程的示例

三、面向过程编程(POP)

简述

面向过程编程就是一种线性思想,按照步骤一步步的去解决问题,也就是面向过程的。对应到代码层面就是一个步骤一个函数。特点是简单,适用于逻辑比较简单的业务。

这里不多做赘述。

四、面向切面编程(AOP)

简述

主要是因为面向对象编程的主要思想就是封装,而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用,但是同时也增加了代码的重复性,两个分装好的内容再想去做同一件事,只能加入重复的代码,而如果不这么做,选择再封装一个独立的方法让他们去调用,就产生了藕合,会影响原有的两个类。因此就希望有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。不够除了运行时,还可以在编译期、类加载期织入如前文所述也可以通过协议的方式来解决此问题

AOP像OOP一样,只是一种编程范式,AOP并没有规定说,实现AOP协议的代码,要用什么方式去实现。不过从技术上来说,面向切面编程基本上都是通过代理机制实现的,对面向对象编程来说是一种十分有益的补充。

关于AOP面向切面编程的详细讲解

五、链式编程

简述

链式编程是指将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。例如:a(1).b(2).c(3),

  • 代表:Masonry框架

链式编程其实就两个要点:

  • Block 作为当前对象的属性。
  • Block 返回值是当前对象。

关于链式编程的详细

链式编程的一个简单示例

六、响应式编程

响应式编程是指不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。

  • 代表:KVO运用

响应式编程的详细

七、函数式编程

函数式编程是把操作尽量写成一系列嵌套的函数或者方法调用。

  • 函数式编程特点:1、可以把函数作为参数传递给另一个函数,也就是所谓的高阶函数。2、可以返回一个函数,这样就可以实现闭包或者惰性计算
  • 代表:AF的网络回调
  • 以上两个特点还仅仅是简化了代码。从代码的可维护性上讲,函数式编程最大的好处是引用透明,即函数运行的结果只依赖于输入的参数,而不依赖于外部状态,因此,我们常常说函数式编程没有副作用。没有副作用有个巨大的好处,就是函数内部无状态,即输入确定,输出就是确定的,容易测试和维护。

以上我们看到,函数式和链式编程实现原理是一样的,都是在调用方法的时候返回一个对象,利用这个返回的对象去调用下一个方法。只不过链式编程在定义的时候是返回一个 【返回值为对象的block

函数式编程初探

函数式编程入门

总结:

我们要知道, 编程语言不过就是工具,编程思想就是操作工具的方法,没有最好的编程语言和最好的编程思想能够解决所有场景的问题,哪门编程思想解决现实问题越快,代码越容易维护,就去使用它,重要的不是用什么编程思想,而是锻炼自己理解现实需求和抽象代码的能力,这才是最优的方式

🌈Cocoapods私有库创建方法总结

🌈Cocoapods私有库创建方法总结 创建Cocoapods私有库有一个全局了解,主要步骤总结如下:

  1. 创建私有Spec仓库
  2. 创建源码库,并git clone到本地
  3. 创建Cocoapods私有库模板
  4. 编写Cocoapods私有库podspec文件
  5. 添加源码和资源文件到指定的文件夹中(每次更改都需要在Example文件夹中pod install一下)
  6. 验证podspec文件是否无误
  7. 移动Cocoapods私有库模板中的文件到源码库
  8. 提交源码到git仓库,并打tag
  9. 将 podspec 提交到spec仓库
  10. 在项目中使用私有库

1、创建私有Spec仓库

执行repo 命令添加私有库Repo 打开终端,在任意目录下执行下面的命令:

pod repo add MOJiBaseSpec http://192.168.1.3/moji-cocoapods/MOJiBaseSpec.git

解释:
pod repo add 【私有库名称】【1.1中创建的远程仓库的git地址】

如果需要更改,用以下命令删除并重复上一步

pod repo remove REPO_NAME

成功后,我们将在以下目录中看到我们所创建的私有库文件夹

~/.cocoapods/repos

2、创建源码库,并git clone到本地

3、创建Cocoapods私有库模板

cd到你要储存项目的文件夹,使用以下命令创建Cocoapods私有库模板

pod lib create MOJiBase

紧接着,会有一些参数需要配置:(语言根据实际情况选择,生成的结构都是一样的,只是生成的默认语言会不一样)

配置完成后,会自动打开创建的项目

4、将pod创建的项目文件copy到MOJiBase本地仓库里

最终如如下图所示:

5、配置podspec文件

打开 MOJiBase本地仓库中的 Example 中的工程

选择 MOJiBasepodspec 文件进行编辑:

相关字段可以查询官方文档【Podspec Syntax Reference】。

以下是MOJiBase.podspec文件示例,仅供参考。

#
# Be sure to run `pod lib lint MOJiBase.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
 s.name           = 'MOJiBase'
 s.version        = '1.3.11'
 s.summary        = 'MOJi基础工具类库,其中包括一些UI功能,后期需要剥离'
 s.swift_version  = '5.0'

# This description is used to generate tags and improve search results.
#  * Think: What does it do? Why did you write it? What is the focus?
#  * Try to keep it short, snappy and to the point.
#  * Write the description between the DESC delimiters below.
#  * Finally, don't worry about the indent, CocoaPods strips it!

 s.description   = <<-DESC
TODO: Add long description of the pod here.
            DESC

 s.homepage      = 'http://192.168.1.3/moji-cocoapods/mojibase.git'
 # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
 s.license       = { :type => 'MIT', :file => 'LICENSE' }
 s.author        = { '赵汉军' => 'zhaohanjun@mail.shareintelli.com' }
 s.source        = { :git => 'http://192.168.1.3/moji-cocoapods/mojibase.git', :tag => s.version.to_s }
 # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

 s.ios.deployment_target = '11.0'

 s.source_files = 'MOJiBase/Classes/**/*'
  
 s.resource_bundles = {
  #'MOJiBase' => ['MOJiBase/Assets/*.png']
  #'MOJiBase' => ['MOJiBase/base-images/*.png']
  'MOJiBase'  => ['MOJiBase/**/*.{xib,bundle}']
#  'MOJiBase' => ['MOJiBase/Assets/base-images.bundle']
 }

 # s.public_header_files = 'Pod/Classes/**/*.h'
  s.frameworks = 'UIKit', 'Foundation'
  s.dependency 'SVProgressHUD'
  s.dependency 'SwiftyJSON'
  s.dependency 'SnapKit'
  
end

相关字段解释:

//私有库对应的版本号
s.version     = '1.3.11'
//私有库描述,最好写一下,不然最终验证的时候会报警告⚠️
s.summary     = 'MOJi基础工具类库,其中包括一些UI功能,后期需要剥离'
//如果使用的是swift,需要指定使用的版本,否则有可能编译不过,最终验证也会报警告
s.swift_version  = '5.0'
//仓库地址,必须得写,否则后面spec文件会上传不成功
s.source      = { :git => 'http://192.168.1.3/moji-cocoapods/mojibase.git', :tag => s.version.to_s }
//源码文件路径
s.source_files = 'MOJiBase/Classes/**/*'
//指定的资源文件读取路径,xib、png、bundle、Assets都需要指定
s.resource_bundles = {
 'MOJiBase' => ['MOJiBase/**/*.{xib,bundle}']
}
//依赖的系统库
s.frameworks = 'UIKit', 'Foundation'
//依赖的第三方库
s.dependency 'SnapKit'

提示:每次更改podspec文件都必须pod install才会生效

6、添加源码和资源文件:

如图所示:

Classes存放所有的代码文件Assets存放所有的资源文件

1、访问图片资源、bundle、xib等文件

1、因为pod会将我们的bundle、xib、png等资源文件打包进pod项目的mainBundle中的Base.framework中,所以不能用以前的方式直接读取图片,而且实用NSBundle.mainBundle也容易和主项目产生冲突。

OC版本:新建NSBundle+Extension分类

+ (NSBundle *)bundleWithBundleName:(NSString *)bundleName podName:(NSString *)podName {
  if (bundleName == nil && podName == nil) {
    @throw @"bundleName和podName不能同时为空";
  }else if (bundleName == nil ) {
    bundleName = podName;
  }else if (podName == nil) {
    podName = bundleName;
  }
   
   
  if ([bundleName containsString:@".bundle"]) {
    bundleName = [bundleName componentsSeparatedByString:@".bundle"].firstObject;
  }
  //没使用framwork的情况下
  NSURL *associateBundleURL = [[NSBundle mainBundle] URLForResource:bundleName withExtension:@"bundle"];
  //使用framework形式
  if (!associateBundleURL) {
    associateBundleURL = [[NSBundle mainBundle] URLForResource:@"Frameworks" withExtension:nil];
    associateBundleURL = [associateBundleURL URLByAppendingPathComponent:podName];
    associateBundleURL = [associateBundleURL URLByAppendingPathExtension:@"framework"];
    NSBundle *associateBunle = [NSBundle bundleWithURL:associateBundleURL];
    associateBundleURL = [associateBunle URLForResource:bundleName withExtension:@"bundle"];
  }
   
  NSAssert(associateBundleURL, @"取不到关联bundle");
  //生产环境直接返回空
  return associateBundleURL?[NSBundle bundleWithURL:associateBundleURL]:nil;
}

Swift版本

extension Bundle {
   
  class func fetchBundle(bundleName: String?, podName: String?) -> Bundle? {
    var bundleName = bundleName
    var podName  = podName
    //判断参数
    if bundleName == nil && podName == nil {
      assert((bundleName != nil), "bundleNameh 和 podName不能同时为空")
    } else if (bundleName == nil) {
      bundleName = podName
    } else if (podName == nil) {
      podName = bundleName
    }
    //适配名称
    if ((bundleName?.contains(".bundle")) != nil) {
      bundleName = bundleName?.components(separatedBy: ".bundle").first
    }
    var associateBundleURL = Bundle.main.url(forResource: bundleName, withExtension: "bundle")
    //如果associateBundleURL为空,则走下面方法
    if associateBundleURL == nil {
      associateBundleURL = Bundle.main.url(forResource: "Frameworks", withExtension: nil)
      associateBundleURL = associateBundleURL?.appendingPathComponent(podName!)
      associateBundleURL = associateBundleURL!.appendingPathExtension("framework")
      let associateBunle = Bundle(url: associateBundleURL!)
      associateBundleURL = associateBunle?.url(forResource: bundleName, withExtension: "bundle")
    }
    assert((associateBundleURL != nil), "取不到关联bundle")

    return Bundle(url: associateBundleURL!) ?? nil
     
  }
}

7、验证podspec文件是否无误

1、主要使用命令

cd 到 MOJiBase项目文件夹下。其目录下有 MOJiBase.podspec 文件。

执行以下命令:

pod lib lint --allow-warnings
#严谨一点,可以不忽略警告⚠️,如下
pod lib lint

如果验证成功将会显示如下图:

如果报Error可以用如下命令查看详细错误信息:

pod lib lint --verbose 

2、常见的几个警告⚠️信息

1、summary: The summary is not meaningful.

解释:这是因为你没有修改 .podspec 文件中的 s.summary 字段。只需要修改 .podspec 文件即可,如下:

s.summary          = '这是一个测试组件库'

2、swift: The validator used Swift 3.2 by default because no Swift version was specified

解释:没有指定Swift版本,只需要在 .podspec 增加对swift版本指定即可,如下:

s.swift_version  = '5.0'

解决完上面两个WARN之后,再执行pod lib lint --allow-warnings 命令就会发现没有警告了。

后续欢迎补充更多“常见错误”以帮助开发小伙伴避坑!

8、提交MOJiBase到gitlab仓库

按照如下步骤进行git操作:

git add --all
git commit -a -m "第一次提交"
git push origin master
git tag 1.3.11
git push --tags

9、将podspec提交到gitlab

cd 到 MOJiBase项目文件夹下。其目录下有MOJiBase.podspec 文件。运行如下命令:

pod repo push BOTestSpec MOJiBase.podspec --allow-warnings
#命令解释
#pod repo push 【私有库名称】 【podspec文件名】 --allow-warnings

上传成功后将会显示如下信息:

显示如上信息后,你还可以查看 ~/.cocoapods/repos。在 MOJiBaseSpec 仓库下会多新增 MOJiBase 文件夹。如下所示:

至此,你的私有库已制作完成。你和你的小伙伴们可以在项目中使用它了。

10、在你的项目中使用你的私有库

1、创建MOJiBaseTest测试项目。

2、在项目目录下,执行命令:

pod init

3、编辑 podfile 文件:

platform :ios, '9.0' 
source 'http://192.168.1.3/moji-cocoapods/mojibase.git' # 官方库
source 'http://192.168.1.3/moji-cocoapods/MOJiBaseSpec.git'# 私有库Repo地址 
target 'BOSpecDemo' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 
use_frameworks! 
# Pods for MOJiBaseTest 
pod 'MOJiBase' 
end

注意:私有仓库的地址一定是Spec Repo 的地址,不要错误的使用MOJiBase组件的git仓库。

然后执行 pod install,添加私有库到工程中。