2020-05-09 09:22:27 xfxf996 阅读数 43

本文翻译自:Any way to replace characters on Swift String?

I'm looking for a way to replace characters in a Swift String . 我正在寻找一种替换Swift String字符的方法。

Example: "This is my string" 示例: "This is my string"

I'd like to replace 我想更换 with + to get: "This+is+my+string" . +可以得到: "This+is+my+string"

How can I achieve this? 我该如何实现?


#1楼

参考:https://stackoom.com/question/1dXku/有什么办法可以替换Swift-String上的字符吗


#2楼

Did you test this : 您是否测试过:

var test = "This is my string"

let replaced = test.stringByReplacingOccurrencesOfString(" ", withString: "+", options: nil, range: nil)

#3楼

This answer has been updated for Swift 4 . 此答案已针对Swift 4进行了更新 If you're still using Swift 1, 2 or 3 see the revision history. 如果您仍在使用Swift 1、2或3,请参阅修订历史记录。

You have a couple of options. 您有两种选择。 You can do as @jaumard suggested and use replacingOccurrences() 您可以按照@jaumard的建议进行操作,并使用replaceOccurrences replacingOccurrences()

let aString = "This is my string"
let newString = aString.replacingOccurrences(of: " ", with: "+", options: .literal, range: nil)

And as noted by @cprcrack below, the options and range parameters are optional, so if you don't want to specify string comparison options or a range to do the replacement within, you only need the following. 就像下面@cprcrack所指出的那样, optionsrange参数是可选的,因此,如果您不想指定字符串比较选项或范围来在其中进行替换,则只需执行以下操作。

let aString = "This is my string"
let newString = aString.replacingOccurrences(of: " ", with: "+")

Or, if the data is in a specific format like this, where you're just replacing separation characters, you can use components() to break the string into and array, and then you can use the join() function to put them back to together with a specified separator. 或者,如果数据采用特定格式,例如您只是替换分隔符,则可以使用components()将字符串分成和数组,然后可以使用join()函数将其放回与指定的分隔符一起使用。

let toArray = aString.components(separatedBy: " ")
let backToString = toArray.joined(separator: "+")

Or if you're looking for a more Swifty solution that doesn't utilize API from NSString, you could use this. 或者,如果您正在寻找一种不使用NSString API的更快速的解决方案,则可以使用它。

let aString = "Some search text"

let replaced = String(aString.map {
    $0 == " " ? "+" : $0
})

#4楼

You can use this: 您可以使用此:

let s = "This is my string"
let modified = s.replace(" ", withString:"+")    

If you add this extension method anywhere in your code: 如果您在代码中的任何位置添加此扩展方法:

extension String
{
    func replace(target: String, withString: String) -> String
    {
       return self.stringByReplacingOccurrencesOfString(target, withString: withString, options: NSStringCompareOptions.LiteralSearch, range: nil)
    }
}

Swift 3: 斯威夫特3:

extension String
{
    func replace(target: String, withString: String) -> String
    {
        return self.replacingOccurrences(of: target, with: withString, options: NSString.CompareOptions.literal, range: nil)
    }
}

#5楼

A category that modifies an existing mutable String: 修改现有可变字符串的类别:

extension String
{
    mutating func replace(originalString:String, withString newString:String)
    {
        let replacedString = self.stringByReplacingOccurrencesOfString(originalString, withString: newString, options: nil, range: nil)
        self = replacedString
    }
}

Use: 采用:

name.replace(" ", withString: "+")

#6楼

If you don't want to use the Objective-C NSString methods, you can just use split and join : 如果您不想使用Objective-C NSString方法,则可以使用splitjoin

var string = "This is my string"
string = join("+", split(string, isSeparator: { $0 == " " }))

split(string, isSeparator: { $0 == " " }) returns an array of strings ( ["This", "is", "my", "string"] ). split(string, isSeparator: { $0 == " " })返回一个字符串数组( ["This", "is", "my", "string"] )。

join joins these elements with a + , resulting in the desired output: "This+is+my+string" . join+将这些元素连接起来,产生所需的输出: "This+is+my+string"

2016-12-13 13:50:24 sinat_20037505 阅读数 7835

 // 这里是var变量, 不能是常量

        var array1 = [1, 2, 3, 4, 5]

        // 这样子就修改了第一个元素

        array1[0] = 9

        

        print(array1)

        

        print("------------>")

        // 删除最后一个

        array1.removeLast()

//        array1.removeAtIndex(1)

//        array1.removeAll()

        

        // 可以保持容量不变

        array1.removeAll(keepingCapacity: true)

        

        print(array1)

        print(array1.count)

        print("capacity==\(array1.capacity)")


2015-11-10 16:14:49 fengzhixinfei 阅读数 1090

学习笔记

四种初始化方式 
let sArray = [1.2,3,4,5,6]
let sArray1:Array = [12,3,4,5,6,6]
let sArray2:Array<Int> = [1,2,3,29]
let sArray3:[Int] = [3,2,21,5,6]

数组元素可以是不同类型
let array1:[Any] = ["a",12,1.2]
let array2:Array< AnyObject> = [1,2,"12",2.3]

数组元素的访问 修改 增加 删除 
var array3:[Any] = [“111”,"222”,"333”,”444”,”aaa"]
print(array3[2]) //访问数组元素 不能越界
print(array3)
array3.insert(1212, atIndex: 2) //插入
print(array3)
array3.append(1.23) //最后位置增加
print(array3)
       
array3[2] = "hello" //修改
print("容量 \(array3.capacity)")
       
array3.removeLast() // 删除最后一个元素
array3.removeFirst() //删除第一个元素
print(array3)
array3.removeFirst(1) //删除从0开始的n个元素
print(array3)
array3.removeAtIndex(1) //删除指定位置元素
print(array3)

数组的批量操作
var nRange:Range<Int> = 1..<3
nRange = Range(start: 1, end: 5)
print(nRange)
       
//数组批量操作
var array4:[AnyObject] = ["aaa",1,2,3,1.2,"bbbbb"]
print(array4)
array4.replaceRange(nRange, with: ["sss",12])
array4.replaceRange(Range(start: 0, end: 1), with: ["sss",12])
print(array4)
array4.replaceRange(1..<3, with: ["ooooooo"])
print(array4)
array4[1...2] = [0,0,0,0,0,0]
print(array4)
array4 += ["a","b","c"]
print(array4)
array4 += array4[0...1]
print(array4)

数组遍历
for elm in array4 {  //遍历数组元素
     print(elm)
}

for (index,element) in array4.enumerate(){
     print("\(index)--\(element)")

2018-06-27 09:13:31 weixin_33763244 阅读数 242

关于 ContiguousArray ,这边有喵神的文章介绍的很详细了,可以先看看这个文章。

Array

接着喵神的思路,看一下 Array 以下是从源码中截取的代码片段。

public struct Array<Element>: _DestructorSafeContainer {
  #if _runtime(_ObjC)
    internal typealias _Buffer = _ArrayBuffer<Element>
  #else
    internal typealias _Buffer = _ContiguousArrayBuffer<Element>
  #endif

  internal var _buffer: _Buffer
  
  internal init(_buffer: _Buffer) {
    self._buffer = _buffer
  }
}
复制代码

if _runtime(_ObjC) 等价于 #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS),从这个操作也可以看出 Swift 的野心不仅仅只是替换 Objective-C那么简单,而是往更加宽泛的方向发展。由于本次主要是研究在 iOS下的开发,所以主要看一下 _ArrayBuffer

_ArrayBuffer

去掉了注释和与类型检查相关的属性和方法。

internal typealias _ArrayBridgeStorage
  = _BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore>

internal struct _ArrayBuffer<Element> : _ArrayBufferProtocol {

  internal init() {
    _storage = _ArrayBridgeStorage(native: _emptyArrayStorage)
  }
  
  internal var _storage: _ArrayBridgeStorage
}
复制代码

可见 _ArrayBuffer 仅有一个存储属性 _storage ,它的类型 _ArrayBridgeStorage,本质上是 _BridgeStorage
_NSArrayCore 其实是一个协议,定义了一些 NSArray 的方法,主要是为了桥接 Objective-CNSArray
最主要的初始化函数,是通过 _emptyArrayStorage 来初始化 _storage

实际上 _emptyArrayStorage_EmptyArrayStorage 的实例,主要作用是初始化一个空的数组,并且将内存指定在堆上。

internal var _emptyArrayStorage : _EmptyArrayStorage {
  return Builtin.bridgeFromRawPointer(
    Builtin.addressof(&_swiftEmptyArrayStorage))
}
复制代码

_BridgeStorage

struct _BridgeStorage<NativeClass: AnyObject, ObjCClass: AnyObject> {
  
  typealias Native = NativeClass
  typealias ObjC = ObjCClass
  
  init(native: Native, bits: Int) {
    rawValue = _makeNativeBridgeObject(
      native, UInt(bits) << _objectPointerLowSpareBitShift)
  }
  
  init(objC: ObjC) {
    rawValue = _makeObjCBridgeObject(objC)
  }
  
  init(native: Native) {
    rawValue = Builtin.reinterpretCast(native)
  }
  
  internal var rawValue: Builtin.BridgeObject
复制代码

_BridgeStorage 实际上区分 是否是 class、@objc ,进而提供不同的存储策略,为上层调用提供了不同的接口,以及类型判断,通过 Builtin.BridgeObject 这个中间参数,实现不同的储存策略。

The ContiguousArray type is a specialized array that always stores its elements in a contiguous region of memory. This contrasts with Array, which can store its elements in either a contiguous region of memory or an NSArray instance if its Element type is a class or @objc protocol.

If your array’s Element type is a class or @objc protocol and you do not need to bridge the array to NSArray or pass the array to Objective-C APIs, using ContiguousArray may be more efficient and have more predictable performance than Array. If the array’s Element type is a struct or enumeration, Array and ContiguousArray should have similar efficiency.

正因为储存策略的不同,特别是在class 或者 @objc,如果不考虑桥接到 NSArray 或者调用 Objective-C,苹果建议我们使用 ContiguousArray,会更有效率。

Array 和 ContiguousArray 区别

通过一些常用的数组操作,来看看两者之间的区别。

append

ContiguousArray

public mutating func append(_ newElement: Element) {
    _makeUniqueAndReserveCapacityIfNotUnique()
    let oldCount = _getCount()
    _reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)
    _appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement)
  }
 
 internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
    if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
      _copyToNewBuffer(oldCount: _buffer.count)
    }
  }
  
  internal mutating func _reserveCapacityAssumingUniqueBuffer(oldCount: Int) {
    let capacity = _buffer.capacity == 0

    if _slowPath(oldCount + 1 > _buffer.capacity) {
      _copyToNewBuffer(oldCount: oldCount)
    }
  }
  
  internal mutating func _copyToNewBuffer(oldCount: Int) {
    let newCount = oldCount + 1
    var newBuffer = _buffer._forceCreateUniqueMutableBuffer(
      countForNewBuffer: oldCount, minNewCapacity: newCount)
    _buffer._arrayOutOfPlaceUpdate(
      &newBuffer, oldCount, 0, _IgnorePointer())
  }
  
  internal mutating func _appendElementAssumeUniqueAndCapacity(
    _ oldCount: Int,
    newElement: Element
  ) {
    _buffer.count = oldCount + 1
    (_buffer.firstElementAddress + oldCount).initialize(to: newElement)
  }
复制代码

_makeUniqueAndReserveCapacityIfNotUnique() 检查数组是否是唯一持有者,以及是否是可变数组。
_reserveCapacityAssumingUniqueBuffer(oldCount: oldCount)检查数组内的元素个数加一后,是否超出超过所分配的空间。
前两个方法在检查之后都调用了 _copyToNewBuffer ,主要操作是如果当前数组需要申请空间,则申请空间,然后再复制 buffer
_appendElementAssumeUniqueAndCapacity(oldCount, newElement: newElement) 从首地址后的第 oldCount 个存储空间内,初始化 newElement

Array

Array 实现的过程与 ContiguousArray 差不多,但是还是有一些区别,具体看看,主要的区别存在于_ContiguousArrayBuffer_ArrayBuffer

_ContiguousArrayBuffer

internal var firstElementAddress: UnsafeMutablePointer<Element> {
  return UnsafeMutablePointer(Builtin.projectTailElems(_storage,
                                                       Element.self))
}
复制代码

直接返回了内存地址。

_ArrayBuffer

internal var firstElementAddress: UnsafeMutablePointer<Element> {
   _sanityCheck(_isNative, "must be a native buffer")
   return _native.firstElementAddress
 }

 internal var _native: NativeBuffer {
   return NativeBuffer(
     _isClassOrObjCExistential(Element.self)
     ? _storage.nativeInstance : _storage.nativeInstance_noSpareBits)
 }
 
internal typealias NativeBuffer = _ContiguousArrayBuffer<Element>
复制代码

从调用的情况来看,本质上还是调用了 _ContiguousArrayBufferfirstElementAddress

但是在创建时,会有类型检查。

_isClassOrObjCExistential(Element.self)检查是否是类或者@objc修饰的。

在上述中检查持有者是否唯一和数组是否可变的函数中, 其实是调用了 _buffer内部的 isMutableAndUniquelyReferenced()

_ContiguousArrayBuffer

 @inlinable
  internal mutating func isUniquelyReferenced() -> Bool {
    return _isUnique(&_storage)
  }
复制代码
internal func _isUnique<T>(_ object: inout T) -> Bool {
  return Bool(Builtin.isUnique(&object))
}
复制代码

最后调用的 Builtin 中的 isUnique

_ArrayBuffer

internal mutating func isUniquelyReferenced() -> Bool {
   if !_isClassOrObjCExistential(Element.self) {
     return _storage.isUniquelyReferenced_native_noSpareBits()
   }
   
   if !_storage.isUniquelyReferencedNative() {
     return false
   }
   return _isNative
 }
 
 mutating func isUniquelyReferencedNative() -> Bool {
   return _isUnique(&rawValue)
 }

 mutating func isUniquelyReferenced_native_noSpareBits() -> Bool {
   _sanityCheck(isNative)
   return _isUnique_native(&rawValue)
 }

func _isUnique_native<T>(_ object: inout T) -> Bool {
  _sanityCheck(
    (_bitPattern(Builtin.reinterpretCast(object)) &  _objectPointerSpareBits)
    == 0)
  _sanityCheck(_usesNativeSwiftReferenceCounting(
      type(of: Builtin.reinterpretCast(object) as AnyObject)))
 return Bool(Builtin.isUnique_native(&object))
}
复制代码

如果是 class 或者 @objc_ContiguousBuffer 一样。如果不是则需要调用 Builtin 中的 _isUnique_native,即要检查是否唯一,还要检查是否是 Swift 原生 而不是 NSArray。 相对于 _ContiguousArrayBuffer 由于 _ArrayBuffer 承载了需要桥接到 NSArray 的功能,所以多了一些类型检查的操作。

insert

ContiguousArray

//ContiguousArray
 public mutating func insert(_ newElement: Element, at i: Int) {
   _checkIndex(i)
   self.replaceSubrange(i..<i, with: CollectionOfOne(newElement))
 }
  
 public mutating func replaceSubrange<C>(
    _ subrange: Range<Int>,
    with newElements: C
  ) where C : Collection, C.Element == Element {
    let oldCount = _buffer.count
    let eraseCount = subrange.count
    let insertCount = newElements.count
    let growth = insertCount - eraseCount

    if _buffer.requestUniqueMutableBackingBuffer(
      minimumCapacity: oldCount + growth) != nil {

      _buffer.replaceSubrange(
        subrange, with: insertCount, elementsOf: newElements)
    } else {
      _buffer._arrayOutOfPlaceReplace(subrange, with: newElements, count: insertCount)
    }
  }
  
  internal mutating func requestUniqueMutableBackingBuffer(
    minimumCapacity: Int
  ) -> _ContiguousArrayBuffer<Element>? {
    if _fastPath(isUniquelyReferenced() && capacity >= minimumCapacity) {
      return self
    }
    return nil
  }
  
  //extension ArrayProtocol
  internal mutating func replaceSubrange<C>(
    _ subrange: Range<Int>,
    with newCount: Int,
    elementsOf newValues: C
  ) where C : Collection, C.Element == Element {
    _sanityCheck(startIndex == 0, "_SliceBuffer should override this function.")
    let oldCount = self.count //现有数组大小
    let eraseCount = subrange.count //需要替换大小

    let growth = newCount - eraseCount //目标大小 和 需要替换大小 的差值
    self.count = oldCount + growth  //替换后的数组大小

    let elements = self.subscriptBaseAddress //数组首地址。
    let oldTailIndex = subrange.upperBound 
    let oldTailStart = elements + oldTailIndex //需要替换的尾地址。
    let newTailIndex = oldTailIndex + growth //需要增加的空间的尾下标
    let newTailStart = oldTailStart + growth //需要增加的空间的尾地址
    let tailCount = oldCount - subrange.upperBound //需要移动的内存空间大小

    if growth > 0 {
      var i = newValues.startIndex
      for j in subrange {
        elements[j] = newValues[i]
        newValues.formIndex(after: &i)
      }
      for j in oldTailIndex..<newTailIndex {
        (elements + j).initialize(to: newValues[i])
        newValues.formIndex(after: &i)
      }
      _expectEnd(of: newValues, is: i)
    }
    else { 
      var i = subrange.lowerBound
      var j = newValues.startIndex
      for _ in 0..<newCount {
        elements[i] = newValues[j]
        i += 1
        newValues.formIndex(after: &j)
      }
      _expectEnd(of: newValues, is: j)

      if growth == 0 {
        return
      }

      let shrinkage = -growth
      if tailCount > shrinkage {   

        newTailStart.moveAssign(from: oldTailStart, count: shrinkage)

        oldTailStart.moveInitialize(
          from: oldTailStart + shrinkage, count: tailCount - shrinkage)
      }
      else {                      
        newTailStart.moveAssign(from: oldTailStart, count: tailCount)

        (newTailStart + tailCount).deinitialize(
          count: shrinkage - tailCount)
      }
    }
  }

复制代码

insert 内部实际是 调用了 replaceSubrange。 而在 replaceSubrange 的操作是,判断内存空间是否够用,和持有者是否唯一,如果有一个不满足条件则复制 buffer 到新的内存空间,并且根据需求分配好内存空间大小。

_buffer 内部的 replaceSubrange

  • 计算 growth 值看所替换的大小和目标大小差值是多少。
  • 如果 growth > 0 ,则需要将现有的内存空间向后移动 growth 位。
  • 替换所需要替换的值。
  • 超出的部分重新分配内存并初始化值。
  • 如果 growth <= 0,则将现有的值替换成新的值即可。
  • 如果 growth < 0,则将不需要的内存空间回收即可。(ps:删除多个元素或者需要替换的大小大于目标大小)。

Array insert 两者基本一致,唯一的区别和 append 一样在 在buffer的内部方法,isUniquelyReferenced() 中,多了一些类型检查。

remove

ContiguousArray

public mutating func remove(at index: Int) -> Element {
    _makeUniqueAndReserveCapacityIfNotUnique()
    let newCount = _getCount() - 1
    let pointer = (_buffer.firstElementAddress + index)
    let result = pointer.move()
    pointer.moveInitialize(from: pointer + 1, count: newCount - index)
    _buffer.count = newCount
    return result
  }
复制代码

检查数组持有者是否唯一,取出所要删除的内存地址,通过将当前的内存区域覆盖为一个未初始化的内存空间,以达到回收内存空间的作用,进而达到删除数组元素的作用。

Array

ContiguousArray 的区别就在于 _makeUniqueAndReserveCapacityIfNotUnique() 前面已经提到过,仍然是多了一些类型检查。

subscript

ContiguousArray

//ContiguousArray
public subscript(index: Int) -> Element {
    get {
      let wasNativeTypeChecked = _hoistableIsNativeTypeChecked()

      let token = _checkSubscript(
        index, wasNativeTypeChecked: wasNativeTypeChecked)

      return _getElement(
        index, wasNativeTypeChecked: wasNativeTypeChecked,
        matchingSubscriptCheck: token)
    }
  }
  
  public
  func _getElement(
    _ index: Int,
    wasNativeTypeChecked : Bool,
    matchingSubscriptCheck: _DependenceToken
  ) -> Element {
  #if false
    return _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked)
  #else
    return _buffer.getElement(index)
  #endif
  }

  //ContiguousArrayBuffer
  internal func getElement(_ i: Int) -> Element {
    return firstElementAddress[i]
  }
复制代码

_hoistableIsNativeTypeChecked() 不做任何检查,直接返回 true_checkSubscript(index, wasNativeTypeChecked: wasNativeTypeChecked) 检查 index 是否越界。 _getElement 最终还是操作内存,通过 firstElementAddress 偏移量取出值。

Array

  //Array
  public
  func _checkSubscript(
    _ index: Int, wasNativeTypeChecked: Bool
  ) -> _DependenceToken {
#if _runtime(_ObjC)
    _buffer._checkInoutAndNativeTypeCheckedBounds(
      index, wasNativeTypeChecked: wasNativeTypeChecked)
#else
    _buffer._checkValidSubscript(index)
#endif
    return _DependenceToken()
  }
  
  func _hoistableIsNativeTypeChecked() -> Bool {
   return _buffer.arrayPropertyIsNativeTypeChecked
  }
  
  //ArrayBuffer
  internal var arrayPropertyIsNativeTypeChecked: Bool {
    return _hasNativeBuffer
  }
  
  internal var _isNativeTypeChecked: Bool {
    if !_isClassOrObjCExistential(Element.self) {
      return true
    } else {
      return _storage.isNativeWithClearedSpareBits(deferredTypeCheckMask)
    }
  }
复制代码

ContiguousArray_hoistableIsNativeTypeChecked() 直接返回 true, 而 Array 中如果不是 class 或者 @objc 会返回 ture,否则会检查是否可以桥接到 Swift

而在 Array_checkSubscript 调用的 _buffer 内部函数也不一样,下面来具体看一看内部实现。

  //ArrayBuffer
  internal func _checkInoutAndNativeTypeCheckedBounds(
    _ index: Int, wasNativeTypeChecked: Bool
  ) {
    _precondition(
      _isNativeTypeChecked == wasNativeTypeChecked,
      "inout rules were violated: the array was overwritten")

    if _fastPath(wasNativeTypeChecked) {
      _native._checkValidSubscript(index)
    }
  }
  
  //ContiguousArrayBuffer
  internal func _checkValidSubscript(_ index : Int) {
    _precondition(
      (index >= 0) && (index < count),
      "Index out of range"
    )
  }
复制代码

本质上就是多了一些是否是类型检查。

//Array
func _getElement(
    _ index: Int,
    wasNativeTypeChecked : Bool,
    matchingSubscriptCheck: _DependenceToken
  ) -> Element {
#if _runtime(_ObjC)
    return _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked)
#else
    return _buffer.getElement(index)
#endif
  }

//ArrayBuffer
internal func getElement(_ i: Int, wasNativeTypeChecked: Bool) -> Element {
    if _fastPath(wasNativeTypeChecked) {
      return _nativeTypeChecked[i]
    }
    return unsafeBitCast(_getElementSlowPath(i), to: Element.self)
  }
  
internal func _getElementSlowPath(_ i: Int) -> AnyObject {
    let element: AnyObject
    if _isNative {
      _native._checkValidSubscript(i)
      
      element = cast(toBufferOf: AnyObject.self)._native[i]
    } else {
      element = _nonNative.objectAt(i)
    }
    return element
  }
  
  //ContiguousArrayBuffer
  internal subscript(i: Int) -> Element {
    get {
      return getElement(i)
    }
  }
复制代码

_buffer 内部的 getElement , 与 ContiguousArray 不同的是需要适配桥接到 NSArray 的情况,如果是 非NSArray 的情况调用的是 ContiguousArrayBuffer 内部的 subscript ,和 ContiguousArray 相同。

总结

从增删改查来看,不管是 ContiguousArray 还是 Array 最终都是操作内存,稍显区别的就是 Array 需要更多的类型检查。所以当不需要 Objective-C,还是尽量使用 ContiguousArray 。 下面是对数组中一些批量操作的总结:

  • removeAllinsert<C>(contentsOf: C, at: Int)removeSubrange:最终调用的是 replaceSubrange
  • append<S : Sequence>(contentsOf newElements: S)init(repeating repeatedValue: Element, count: Int):最终都是操作内存,循环初始化新的内存空间和值。

有什么不正确的地方,欢迎指出。

2015-09-09 14:58:51 CODE_WORM 阅读数 6276


/*

编程环境: xcode 7.0 beta6

*/




/*---------------------------.数组的创建-------------------------*/

//[someType]  等同于 Array<someType>,都代表可以装有someType这种类型数据的数组,下边我只使用[someType]这种形式:


/*

1. 变量数组,常量数组

变量数组可以进行更改,对应OC可变数组:NSMutableArray,常量数组不可以进行修改, 对应OC不可变数组:NSArray

*/

var arr1_1:[Int]     //变量数组

let arr1_2: [Int]    //常量数组,可以不直接赋值, 但是当第一次赋值过后就不可以更改了


/*

2. 数组的创建你可以指定这个数组装有什么类型,如果你指定特定的类型, 那么这个数组只能装这个类型的数据,或者这个类型的子类的数据;

*/

let arr1_3: [String] = ["ab","cd"] //只能添加字符串类型


/*

3. 如果你不指定的类型, swift会有类型推断,根据你在等号右边赋的值来进行类型推断:

*/

//(1). 如果赋的值都是某个特定类型的数据,比如说都是[1, 2, 3], 那么类型推断为[Int];

var arr1_4 = [1,2, 3]    //这时arr1_4[Int]类型

arr1_4 = []              //这种情况, arr1_4依旧是[Int]类型


//(2). 如果你所赋的值都为NSObject类的对象或者继承与NSObject类的对象,或者String, Int, Double等一些基础类型, 那么swift将这个数组推断为[NSObject]类型;

let NSStr1_1: NSString ="NSString"

let arr1_5 = [1,"123", NSStr1_1]   //这时arr1_5[NSObject]类型


//(3). 如果数组中包含不止继承与NSObject类的数据(没有任何继承与任何类的对象),或者不继承与NSObject又不是基础类型数据,比如说Array Dictionary, swift将其推断为NSArray类型(原来是OC中的类型, swift中一样有这个类型, 实现了部分OC中的方法), NSArray这个类型可以和swift中的[Element]相互转换, [Element](这个涉及到泛型, 就是一个占位类型, 你往数组添加任何类型的数据都可以);

let arr1_6 = [1,"123", arr1_5]   //识别为NSArray类型

let arr1_7 = Array(arr1_6)        //将其转换为[Element]类型,注意[Element]这个类型不可以直接声明;

/*如果想得到一个,空的泛型数组可以这样写:*/

let arr1_8 = []             //这个时候识别为NSArray类型

var arr1_9 = Array(arr1_8//arr1_9就是一个空的[Element]类型的数组


//4. 创建一个有多个元素重复的数组

var arr1_10 = Array(count:3, repeatedValue: 1.3)

print(arr1_10)

/*打印结果

[1.3, 1.3, 1.3]  //含有3 1.3的数组

*/


//5. 通过两个数组相拼接的方式获取新数组

var arr1_11 = arr1_7 + arr1_9//等号后边两个数组必须是相同类型的数组,比如这两个都是泛型数组[Element]

print(arr1_11)

/*打印结果

[1, 123, (

1,

123,

NSString

)]

*/


//6. 通过 Array(arrayLiteral: <#T##Element...##Element#>)来创建数组:

var NSStr1_2: NSString ="NSString2"

let arr1_12 = Array(arrayLiteral:1, "123",1.23, NSStr1_2)     //这后边只能填NSObject类的对象或者继承与NSObject类的对象,以及Int, Double, String 一些基础类型,这时arr1_12被识别为[NSObject]类型


//7. 通过范围运算符 0...1来创建数组:

//0...1 表示 0 1 的开区间(0..<1表示0 1 的半开半闭区间,不包括1)

let arr1_13 = [0,1, 2, 3, 4, 5]

var arr1_14 = arr1_13[0...3]   //这个时候arr1_14ArraySlice类型(ArraySlice类型和Array类型很像,像截取数组的一部分的时候, 都会被识别为ArraySlice类型,可以被理解为迷你版的Array, Array ArraySlice 类型和可以相互转换)

var arr1_15 = Array(arr1_14)  //转换为Array<Int>类型




/*---------------------------.遍历数组-------------------------*/


//1 首先说一下如何查看数组的元素,以及获取数组的一些基础属性

var arr2_1 = [999,10, 99, 4]

//(1)首先说一下数组名[下标]的方式来访问或者修改数组元素 eg:

print(arr2_1[0])

/*打印结果

999

*/


arr2_1[0] =110

print(arr2_1)

/*修改后的打印结果

[110, 10, 99, 4]

*/


//(2)通过0...1(这个表示0 1 的开区间)的这种范围方式来访问数组:

print(arr2_1[0...1])

/*打印结果

[110, 10]

*/


//(3)获取数组某些特定位置的元素:

print(arr2_1.first)    //获取数组的第一个元素    打印结果: Optional(110)

print(arr2_1.last)     //获取数组的最后一个元素  打印结果: Optional(4)


//(4)获取数组元素的数量:

print(arr2_1.count)   //打印结果: 4


//2. for in 数组遍历

//(1)for in 遍历全部元素

for id in arr2_1 {

    print(id)

}

/*打印结果

999

10

99

4

*/


//(2)for in 遍历数组某一个区间的元素

for id in arr2_1[1...3]{

    print(id)

}

/*打印结果:

10

99

4

*/


//3. for 循环数组遍历

for var i =0; i < arr2_1.count; ++i {

    let id = arr2_1[i]

    print(id)

}

/*打印结果

999

10

99

4

*/


//4. 使用枚举法,进行遍历

for (a, b) inarr2_1.enumerate(){

    print("\(a) =\(b)")

}

/*打印结果

0 = 999

1 = 10

2 = 99

3 = 4

*/


//5. 模拟系统给出的枚举法遍历数组

var en = arr2_1.enumerate().generate()

var temp: (Int,Int)? = en.next()

while temp !=nil {

    print(temp!)

    temp = en.next()

}

/*打印结果

(0, 999)

(1, 10)

(2, 99)

(3, 4)

*/




/*---------------------------.给数组添加元素-------------------------*/

//1. 在数组的后边添加一个元素

var arr3_1 = ["hello"]

arr3_1.append("world")

print(arr3_1)

/*打印结果:

[hello, world]

*/


//2. 在数组的某个位置添加元素:

arr3_1.insert("my", atIndex:1)

print(arr3_1)

/*

[hello, my, world]

*/


//3.使用 +=给数组拼接另一个数组在老版本是可以使用 +=拼接一个元素的, 但这个版本把这个功能去掉了

arr3_1 += ["codeWorm","dear"]

print(arr3_1)

/*打印结果:

[hello, my, world, codeWorm, dear]

*/


//4. 在数组的某个index位置,添加一个相同类型的数组:

arr3_1.insertContentsOf(["123","456"], at:0)

print(arr3_1)

/*打印结果:

["123", "456", "hello", "my", "world", "codeWorm", "dear"]

*/




/*---------------------------.删除数组的元素-------------------------*/

var arr4_1 = [1,2, 3, 4, 5]


//1. 删除最后一个元素,返回值为被删除的元素

let remove1 = arr4_1.removeLast()

print(arr4_1)

/*打印结果

[1, 2, 3, 4]

*/


//2. 删除某个index的元素,返回值为被删除的元素

let remove2 = arr4_1.removeAtIndex(0)

print(arr4_1)

/*打印结果

[2, 3, 4]

*/


//3. 删除某个范围的元素

arr4_1.removeRange(Range(start:0, end: 1))

print(arr4_1)

/*打印结果

[3, 4]

*/


//4. 删除数组的所有元素,并且不保留数组所占的空间

arr4_1.removeAll()


//5. 删除数组的所有元素,但是所占的内存空间将被保留

arr4_1.removeAll(keepCapacity:true)




/*---------------------------.修改数组的元素-------------------------*/

var arr5_1 = [1,2, 3, 4, 5, 6,7]


//1. 可以用数组名[下标数]这种形式 来修改某个index的元素值

arr5_1[0] =99 //将第0个元素的值修改为99

print(arr5_1)

/*打印结果

[99, 2, 3, 4, 5, 6, 7]

*/



//2. 将数组某个范围的元素替换为后面的这段数组的元素,注意这个范围最大数不能超过数组的count - 1否则会运行时崩溃

arr5_1[1...3] = [100,101, 102]

print(arr5_1)

/*打印结果

[99, 100, 101, 102, 5, 6, 7]

*/


//3. 使用 arr5_1.replaceRange(<#T##subRange: Range<Int>##Range<Int>#>, with: <#T##C#>) 来替换元素, 这里第一个参数为范围, 第二个参数是要替换的数组或者表示数组的指针

arr5_1.replaceRange(Range(start:0, end: 4), with: [0,1, 2, 3, 4])

print(arr5_1)

/*打印结果

[0, 1, 2, 3, 4, 5, 6, 7]

*/


//replaceRange 替换UnsafeMutableBufferPointer数组的指针类型:

var arr5_2 = [999,1000, 1001]

var pointer = UnsafeMutableBufferPointer<Int>(start: &arr5_2, count:arr5_2.count)    //这是用来表示字符串的指针类型

arr5_1.replaceRange(0..<1, with: pointer)    //第一个参数range的另一种表现形式, 0 1 的半开半闭区间(0...1 表示0 1的开区间), 这里也就是表示, arr5_1的第0个元素替换成这个数组指针对应数组的元素

print(arr5_1)

/*打印结果

[999, 1000, 1001, 1, 2, 3, 4, 5, 6, 7]

*/





/*---------------------------.对于数组的一些基础的判断-------------------------*/


//1. 判断数组是否为空

var arr6_1: [Int] = []

print(arr6_1.isEmpty)//判断的前提是数组已经被实例化

/*打印结果

true

*/


//2. 判断数组是否包含某个元素:

var arr6_2 = ["hello","world", "I","am", "code"]

//(1)可以直接这么调用:

print(arr6_2.contains("hello"))

/*打印结果

true

*/

//(2) 可以使用闭包为参数进行判断

let isContain =arr6_2.contains { (obj) -> Boolin      //这个运行原理就是,编译器调用你的闭包,将数组内的元素顺次作为闭包的参数传入, 进行判断, 当然判断逻辑是你写的,直到闭包的返回值为true 或者将数组内的所有元素传参完毕闭包调用结束,否则继续传入下一个数组的元素作为参数调用这个闭包,如果闭包的返回值出现为true的情况,contains返回值为true,如果直到闭包调用完毕都没有出现true的情况,contains返回值为false

    if obj == "木哈哈" {

        return true

    }

    return false

}

//简写:

let isContainSam =arr6_2.contains{$0 =="木哈哈"}   //$0 就是第一个参数的意思, $1第二个, 多个参数同理, 当一个闭包只有一个return语句时, return可以省略

print(isContain)

/*打印结果

false

*/




/*---------------------------.数组分割为字符串, 字符串拼接数组-------------------------*/

//这个如果有OC基础就知道这个:

//1. 使用某个字符串来分割字符串,分割结果为数组:

let str7_1 = "www.baidu.com/www.v1.cn/haha"

let arr7_1 = str7_1.componentsSeparatedByString(".")

print(arr7_1)

/*打印结果

["www", "baidu", "com/www", "v1", "cn/haha"]

*/


//2. 使用NSCharacterSet类型来分割字符串:

let arr7_2 = str7_1.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString:"./"))    //分别以. /来分割

print(arr7_2)

/*打印结果

["www", "baidu", "com", "www", "v1", "cn", "haha"]

*/


//3. 将数组拼接成字符串

let str7_2 = arr7_2.joinWithSeparator("我是 (空格)")

print(str7_2)

/*打印结果

www我是 (空格)baidu我是 (空格)com我是 (空格)www我是 (空格)v1我是 (空格)cn我是 (空格)haha

*/




/*---------------------------.数组的一些其他方法-------------------------*/


//1. sortInPlace 排序

var arr8_1 = [2,32, 1, 5, 3, 4]

//sortInPlace方法就是原来的sort方法

arr8_1.sortInPlace { (id1, id2) ->Bool in     //传入闭包的原理类似与将冒泡排序的排序准则传入,编译器来实现冒泡排序, 然后根据你的准则来判断是否交换元素位置, eg:

    if id1 > id2{

        return true

    }

    return false

}

//上边这个可以简写为:

//arr8_1.sortInPlace {$0 > $1}       //$0 就是第一个参数的意思, $1 第二个, 多个参数同理,当一个闭包只有一个return语句时, return可以省略

print(arr8_1)

/*打印结果

[32, 5, 4, 3, 2, 1]

*/


//2. reverse 翻转数组:

let arr8_2 = [9,6, 3, 0, 2, 4,5]

let arr8_3 = Array(arr8_2.reverse())

print(arr8_3)

/*打印结果:

[5, 4, 2, 0, 3, 6, 9]

*/


//3. filter 过滤数组:

let arr8_4 = [-5, -4, -3, -2,0, 1, 2, 3, 4,5, 6, 7, 8]

let arr8_5 = arr8_4.filter{$0 %2 == 0 && $0 >0//过滤出数组中的正偶数

//展开为:

//let arr8_5 = arr8_4.filter { (obj) -> Bool in

//    return obj % 2 == 0 && obj > 0

//}

print(arr8_5)

/*打印结果:

[2, 4, 6, 8]

*/


//4. map 遍历计算数组

let arr8_6 = [1,2, 3, 4, 5]

let arr8_7 = arr8_6.map{$0 *2} //通过计算形成新数组

print(arr8_7)

/*打印结果:

[2, 4, 6, 8, 10]

*/


//5. reduce 方法

let arr8_8 = [2.0 ,9.0]

let result8_1 = arr8_8.reduce(9.0) { (obj1, obj2) ->Double in

    return obj1/obj2

}

//reduce, 需要传一个初始值,这里传的是9.0, 第一次传入闭包两个参数分别为初始值本身 数组的第一个元素,第二次传的两个参数分别为, 第一次闭包的运算结果和数组的第二个元素,直到所有数组的元素全部被运算完毕, 最后一次调用闭包完毕返回的值为最终的值, 这里做的计算也就是: 9.0/2.0/9.0 = 0.5


//简写为:

//let result8_1 = arr8_8.reduce(9.0){$0/$1}    这里涉及到尾随闭包的知识, 在网上搜一下尾随闭包就知道怎么回事了


print(result8_1)

/*打印结果:

0.5

*/




/*---------------------------.数组拷贝-------------------------*/

/*

swift刚一出现的时候,数组是作为引用类型出现的, 虽然作为一个结构体,但是更像是类的对象的性质, 这可见数组的这个结构体的底层不单单是像C语言和OC中在栈区开辟空间去存储值类型那么简单,猜测也是要去堆区开辟空间的, 在后来更新版本中, Array取消了这样的性质,而是只作为值类型出现, 这样就让新学习swift的人来说不会有"为什么值类型还可以做引用的操作?"这样的困惑, 下面是一个eg:

*/

var arr9_1 = [1,2, 3]

var arr9_2 = arr9_1

arr9_2[0] =999

print("arr9_1 =\(arr9_1) arr9_2 =\(arr9_2)")

/*打印结果:

arr9_1 = [1, 2, 3] arr9_2 = [999, 2, 3]

*/


//可见现在的数组是一个值类型了!



Swift--数组

阅读数 245