swift 进阶知识点

本文的知识点会比较散,是基础语法之外的一些进阶内容,如果有写的不妥的地方,欢迎评论区指正~

Optional

可选值是通过枚举实现的:

enum Optional<Wrapped> {
	case none
	case some(Wrapped)

对于Optional类型的值可以通过switch来处理,比如case .some(Wrapped),简写为case let Wrapped?,当然解包可选值最简单的方法莫过于用if/guard let的语法。

当需要可选值的时候,传入非可选值会隐式转换成可选值。

while let当条件返回nil时终止循环:

let array = [1, 2, 3]
var iterator = array.makeIterator()
while let i = iterator.next() {
	print(i, terminator: " ")
} // 1 2 3

还有一种for...where的用法,表示满足条件才会执行循环:

for i in 0..<10 where i % 2 == 0 {
	print(i, terminator: " ")
} // 0 2 4 6 8

双重可选值

let stringNumbers = ["1", "2", "three"]
let maybeInts = stringNumbers.map { Int($0) } // [Optional(1), Optional(2), nil]

如果用 maybeInts.makeIterator().next()遍历元素,其实每次返回的都是Int??,相当于解两次包。
如果想筛选出数组中不为nil的元素,可以用:

for case let i? in maybeInts {
// i 将是 Int 值,而不是 Int?
	print(i, terminator: " ")
}
// 1 2

Never & Void & nil

  • public enum Never { }:无法构建Never类型的值,绝对不会返回。guard语句的else路径必须退出当前域或者调用一个不会返回的函数,如fatalError()
  • public typealias Void = ():不返回任何值。
  • nil表示不存在。
var dictWithNils: [String: Int?] = [
"one": 1,
"two": 2,
"none": nil
]

如果dictWithNils["two"] = nil,则字典会删除该项。如果想让"two"对应的值为nil,可以dictWithNils["two"] = .some(nil)dictWithNils["two"]? = nil
这两种赋值的前提都是先保障字典key对应的可选值’存在’,然后给对应的值Int?赋值,而如果直接给dictWithNils["two"] 赋值,赋的只是最外层字典本身的可选值,一旦这个值为nil,那么该项也就不存在了。

Closure

闭包 = 函数 + 捕获的其局部变量外的变量。如果将闭包作为函数参数进行传递,有如下简写方式:

例如[1, 2, 3].map { $0 * 2 } // [2, 4, 6]

  1. 如果编译器可以从上下文中推断出类型的话,你就不需要指明它了。在我们的例子中,从数组元素的类型可以推断出传递给 map的函数接受 Int 作为参数,从闭包内的乘法结果的类型可以推断出闭包返回的也是 Int
  2. 如果闭包表达式的主体部分只包括一个单一的表达式的话,它将自动返回这个表达式的结果,你可以不写 return
  3. Swift 会自动为函数的参数提供简写形式,$0 代表第一个参数,$1 代表第二个参数,以此类推。
  4. 如果函数的最后一个参数是闭包表达式的话,你可以将这个闭包表达式移到函数调用的圆括号的外部,这样的尾随闭包语法 (trailing closure syntax) 在多行的闭包表达式中表现非常好。
  5. 最后,如果一个函数除了闭包表达式外没有别的参数,那么调用的时候在方法名后面的圆括号也可以一并省略。

自动闭包

Swift 中定义一个和 && 操作符具有相同功能的 and 函数:

func and(_ l: Bool, _ r: () -> Bool) -> Bool {
	guard l else { return false }
	return r()
}

我们可以使用 @autoclosure 标注来告诉编译器它应该将一个特定的参数用闭包表达式包装起来。

func and(_ l: Bool, _ r: @autoclosure () -> Bool) -> Bool {
	guard l else { return false }
	return r()
}

过度使用自动闭包可能会让你的代码难以理解,使用时的上下文和函数名应该清晰地指出实际求值会被推迟。

逃逸闭包

一个被保存在某个地方 (比如一个属性中) 等待稍后再调用的闭包就叫做逃逸闭包。相对的,永远不会离开一个函数的局部作用域的闭包就是非逃逸闭包。闭包参数默认是非逃逸的。如果你想要保存一个闭包稍后再用,你需要将闭包参数标记为 @escaping

对于那些使用闭包作为参数的函数,如果闭包被封装到像是元组或者可选值等类型的话,这个闭包参数也是逃逸的。因为在这种情况下闭包不是直接参数,它将自动变为逃逸闭包。这样的结果是,你不能写出一个函数,使它接受的函数参数同时满足可选值和非逃逸。很多情况下,你可以通过为闭包提供一个默认值来避免可选值。如果这样做行不通的话,可以通过重载函数,提供一个包含可选值 (逃逸) 的函数,以及一个不是可选值,非逃逸的函数来绕过这个限制:


// 如果用 nil 参数 (或者一个可选值类型的变量) 来调用函数,将使用可选值变种,而如果使用闭包字面量的调用将使用非逃逸和非可选值的重载方法
func transform(_ input: Int, with f: ((Int) -> Int)?) -> Int {
	print("使用可选值重载")
	guard let f = f else { return input }
	return f(input)
}

func transform(_ input: Int, with f: (Int) -> Int) -> Int {
	print("使用非可选值重载")
	return f(input)
}

捕获列表

闭包可以捕获并存储其定义上下文中任何常量和变量的引用。

闭包的生命周期从它们被创建和赋值开始,一直持续到它们被销毁(即,它们已经没有任何引用指向它们)。

关于闭包与引用循环(Reference cycles)的关系,首先需要了解闭包是引用类型,不是值类型。当一个闭包被赋值给一个变量,常量或者属性,实际上被赋值的是对该闭包的引用,而不是闭包的副本。

例如,如果一个类实例有个属性指向一个闭包,并且这个闭包又捕获(也就是引用)了这个类的实例,那么就会形成一个引用循环。

通过捕获列表可以避免循环引用:

counter = 0
g = {[c = counter] in print(c)}
counter = 1
g() // 0

捕获列表位于闭包的开始部分,包含在方括号([])中,每项由一对由等号分隔的元素组成,前面的元素是引用的名称(可能带有 weak 或 unowned 的标记),后面的元素是在闭包外部的实际变量或常量。

Custom operator

infix operator用来自定义一个中缀运算符。中缀运算符是处于两个操作数之间的运算符,比如加法运算符。

// 自定义运算符**表示取幂运算(即lhs的rhs次幂),并且其优先级与乘法运算相同(MultiplicationPrecedence)。
infix operator **: MultiplicationPrecedence // 使用已有的优先级group
func **(base: Int, power: Int) -> Int {
  precondition(power >= 2)
  var result = base
  for _ in 2...power {
    result *= base
  }
  return result
}

注意:Swift 中定义的运算符必须要有一个优先级组(precedence group)。上述代码中使用了已有的乘法优先级,你也可以自定义优先级:

precedencegroup PowerPrecedence {
  associativity: right // 2 ** 3 ** 2 从右向右结合
  higherThan: MultiplicationPrecedence
}
infix operator **: PowerPrecedence

let result = 2 * 3 ** 2 就会首先计算 3 ** 2

如果想让 ** 支持更过的类型,可以考虑范型:

func **<T: BinaryInteger>(base: T, power: Int) -> T {
  precondition(power >= 2)
  var result = base
  for _ in 2...power {
    result *= base
  }
  return result
}

inout

如果想在函数体内改变参数的值,可利用inout关键词。对于inout参数,你只能传递左值,因为右值是不能被修改的。当你在普通的函数或者方法中使用inout时,需要显式地将它们传入:即在每个左值前面加上&符号。

// ++ 自增运算符
postfix func ++(x: inout Int) {
x += 1
}

需要注意几点:

  • 不能够让这个inout参数逃逸,只能在函数返回前修改
  • 就像对待普通的参数一样,Swift 还是会复制传入的 inout 参数,但当函数返回时,会用这些参数的值覆盖原来的值。也就是说,即使在函数中对一个 inout 参数做多次修改,但对调用者来说只会注意到一次修改的发生,也就是在用新的值覆盖原有值的时候。同理,即使函数完全没有对 inout 参数做任何的修改,调用者也还是会注意到一次修改 (willSetdidSet 这两个观察者方法都会被调用)。

Subscripts

[]操作符进行扩展,可以操作类、结构体、枚举等类型的属性。

class Person {
  let name: String
  let age: Int
  init(name: String, age: Int) {
    self.name = name
    self.age = age
  }
}

extension Person {
  subscript(key: String) -> String? {
    switch key {
      case "name": return name
      case "age": return "\(age)"
      default: return nil
	}
  }
}
let me = Person(name: "Cosmin", age: 36)
me["name"]
me["age"]

// Subscript parameters
subscript(key key: String) -> String? {
  // original code
}
me[key: "name"]
me[key: "age"]

Dynamic member lookup

Subscripts的使用转变为.操作符,做法如下:

// 1
@dynamicMemberLookup
class Instrument {
  let brand: String
  let year: Int
  private let details: [String: String]
  init(brand: String, year: Int, details: [String: String]) {
    self.brand = brand
    self.year = year
    self.details = details
}
// 2 可添加 class 前缀等价于 static 
  subscript(dynamicMember key: String) -> String {
    switch key {
      case "info": return "\(brand) made in \(year)."
      default: return details[key] ?? ""
    }
} }

KeyPath

Setting properties

键路径表达式以一个反斜杠开头,比如 \String.count。反斜杠是为了将键路径和同名的类型属性区分开来。类型推断对键路径也是有效的,在上下文中如果编译器可以推断出类型的话,你可以将类型名省略,只留下 \.count。”

class Tutorial {
  let title: String
  let details: (type: String, category: String)
  init(title: String,
       details: (type: String, category: String)) {
    self.title = title
    self.details = details
  }
}
// 通过 keyPath 可以直接获取和修改属性值
let tutorial = Tutorial(
  title: "Object Oriented Programming in Swift",
  details: (type: "Swift",
          category: "iOS")
)
let title = \Tutorial.title
let tutorialTitle = tutorial[keyPath: title]          

Keypath member lookup

KeyPath 可以和 MemberLookup 结合使用:

struct Point {
  let x, y: Int
}

@dynamicMemberLookup
struct Circle {
  let center: Point
  let radius: Int
  
  subscript(dynamicMember keyPath: KeyPath<Point, Int>) -> Int {
    center[keyPath: keyPath]
  }
}

let center = Point(x: 1, y: 2)
let circle = Circle(center: center, radius: 1)
// 应该是利用了类型推断
circle.x
circle.y

Keypaths as functions

KeyPath 可以当作函数使用:

let titles = [tutorial].map(\.title)

NSObjectobserve(_:options:changeHandler:) 方法将会对一个 \键路径进行观察,并在属性发生变化的时候调用 handler。不要忘记还需要将要观察的属性标记为 @objc dynamic,否则 KVO 将不会工作。

键路径可以帮助我们在两个 NSObject 之间实现双向绑定,键路径可以让我们的代码更加泛用,而不必拘泥于某个特定的属性:

// 通过扩展 NSObjectProtocol 而不是 NSObject,我们可以使用 Self
extension NSObjectProtocol where Self: NSObject {
    
    func observe<A, Other>(_ keyPath: KeyPath<Self, A>,
                       writeTo other: Other,
                      _ otherKeyPath: ReferenceWritableKeyPath<Other, A>)
    -> NSKeyValueObservation
    where A: Equatable, Other: NSObjectProtocol
    {
    	// 注意options的取值,需要在新值变化时调用handler 
        return observe(keyPath, options: .new) { _, change in
            guard let newValue = change.newValue,
                  other[keyPath: otherKeyPath] != newValue else {
                return // prevent endless feedback loop
            }
            other[keyPath: otherKeyPath] = newValue
        }
    }

    func bind<A, Other>(_ keyPath: ReferenceWritableKeyPath<Self,A>,
    to other: Other,
    _ otherKeyPath: ReferenceWritableKeyPath<Other,A>)
    -> (NSKeyValueObservation, NSKeyValueObservation)
    where A: Equatable, Other: NSObject
    {
        let one = observe(keyPath, writeTo: other, otherKeyPath)
        let two = other.observe(otherKeyPath, writeTo: self, keyPath)
        return (one,two)
    }
}

Property Wrappers

@propertyWrapper 是 Swift 中的一个特性,用于自定义属性的获取和设置行为。

@propertyWrapper
struct ZeroTo<Value: Numeric & Comparable> {
  private var value: Value
  let upper: Value

  init(wrappedValue: Value, upper: Value) {
    value = wrappedValue
    self.upper = upper
  }

  var wrappedValue: Value {
    get { min(max(value, 0), upper) }
    set { value = newValue }
  }

  // 可以通过 $ 获取该值, 没必要和 wrappedValue 是一个类型
  var projectedValue: Value { value }
}

// 当我们在函数或闭包的参数中使用属性包装器时,编译器将帮我们生成其他必要的初始化代码。当我们调用函数时,实际参数的值将用作 wrappedValue值。
func printValueV3(@ZeroTo(upper: 10) _ value: Double) {
  print("The wrapped value is", value)
  print("The projected value is", $value)
}
printValueV3(42) // 10

projectedValue 的使用示例如下:

@propertyWrapper
public struct ValidatedDate {
  private var storage: Date? = nil
  private(set) var formatter = DateFormatter()
  public init(wrappedValue: String) {
    self.formatter.dateFormat = "yyyy-mm-dd"
    self.wrappedValue = wrappedValue
  }
  public var wrappedValue: String {
    set {
      self.storage = formatter.date(from: newValue)
    }
    get {
      if let date = self.storage {
        return formatter.string(from: date)
      } else {
        return "invalid"
      }
    }
  }
  public var projectedValue: DateFormatter {
    get { formatter }
    set { formatter = newValue }
  }
}

Protocol

extension protocol 不仅可以为 protocol 提供默认实现,而且可以新增属性、方法的定义和实现。

associatedtype 用于在协议定义中声明一个占位类型,也就是类型的别名。具体的类型在协议被具体类型(如:结构体、类或枚举)遵守时才会被确定:

protocol Container {
    associatedtype Item
    mutating func append(_ item: Item)
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}

struct IntArray: Container {
    // 在遵守协议的类型中指定 Item 实际上是 Int 类型
    typealias Item = Int

    var items = [Int]()

    mutating func append(_ item: Int) {
        items.append(item)
    }

    var count: Int {
        return items.count
    }

    subscript(i: Int) -> Int {
        return items[i]
    }
}

由于协议只是定义了一套共用的接口,很难将实现协议的类型关联起来,所以associatedtype 也可以为协议绑定特定类型的数据。

protocol Product {}
protocol ProductionLine  {
  func produce() -> Product
}
protocol Factory {
  var productionLines: [ProductionLine] {get}
}
extension Factory {
  func produce() -> [Product] {
    var items: [Product] = []
    productionLines.forEach { items.append($0.produce()) }
    print("Finished Production")
    print("-------------------")
    return items
} }

创建一个工厂,里面的产品线可以是任意的,但是我想让一个特定的工厂只生产特定的产品,就可以采用associatedtype 或者generics

protocol Product {
init() }
protocol ProductionLine {
  associatedtype ProductType
  func produce() -> ProductType
}
protocol Factory {
  associatedtype ProductType
  func produce() -> [ProductType]
}
struct GenericProductionLine<P: Product>: ProductionLine {
  func produce() -> P {
    P()
  }
}
struct GenericFactory<P: Product>: Factory {
  var productionLines: [GenericProductionLine<P>] = []
  func produce() -> [P] {
    var newItems: [P] = []
    productionLines.forEach { newItems.append($0.produce()) }
    print("Finished Production")
    print("-------------------")
    return newItems
  }
}

Opaque return types

函数的返回值可以是遵循某些协议的非固定类型:

// return 的必须要是同一类型数据
func makeValueRandomly() -> some FixedWidthInteger {
  if Bool.random() {
    return Int(42)
  }
else {
    return Int(24)
  }
}

有两点需要注意:

  • 不能把返回值作为函数参数
  • 不能够约束返回值的associatedtype,比如some Collection

Type Erasure

在 Swift 中,由于它是一个强类型语言,每一个类、结构体或者枚举都有自己所特有的类型。然而,往往在实际编程中,我们可能需要去抹除这些特定的类型信息,将不同的类型以一种统一的方式去处理。这就是所谓的 “类型擦除”。

类型擦除主要用在两个方面:

  • 将拥有相同行为或特性,但是类型不同的实例,包装成一种通用的类型。比如 Array、Set、Dictionary 都遵循 Collection 协议,我们可以把它们统一的看作是 AnyCollection。
  • 在泛型编程中,更好的隐藏类型的具体细节,提供更加清晰和方便的公共接口。

AnyCollection 的实现为例:

// 私有协议,抓住了 Collection 的公共接口
private protocol _AnyCollectionBox {
    associatedtype Element
    // 定义的其他需要实现的方法和属性
}

// 私有类,实现了私有协议,并持有一个 Collection
private class _CollectionBox<Base : Collection> : _AnyCollectionBox {
    var _base: Base
    init(_ base: Base) {
        _base = base
    }
    // 实现了其他的方法和属性
}

public struct AnyCollection<Element> : Collection {
    // 中间抽象,可以指向任何一个 Collection
    internal var _box: _AnyCollectionBox<Element>
    // 公共的构造方法,接收一个 Collection
    public init<C : Collection>(_ collection: C) where C.Element == Element {
        // 将接收到的 Collection 封装到 box 中
        _box = _CollectionBox(collection)
    }
    // 其他 Collection 协议的方法和属性
}

可以通过以下代码把承载不同元素的容器放到同一个数组里:

let array = Array(1...10)
let set = Set(1...10)
let reversedArray = array.reversed()
let arrayCollections = [array, Array(set), Array(reversedArray)]
let collections = [AnyCollection(array),
                   AnyCollection(set),
                   AnyCollection(array.reversed())]

我对类型擦除的理解就是,对于内部有associatedtype的协议而言,不能把他们放到一个数组里,编译器无法确定数组中的每个元素的具体类型,这是不被 Swift 允许的。所以才有了类型擦除(或许只是其中一个原因)。

额外扯一下AnyAny (或其他的泛型类型)并不是直接存储在数组中的,而是经过了一层封装。在 Array 的底层实现中,所有的元素都会被看作 AnyObject 类型的实例并存储在堆中。这就意味着这个 Array 是一个引用语义类型的集合,且每个元素实际上并不位于连续的内存空间中 – 相反,每个元素是一个指向堆中存储空间的引用。

当我们说 “数组中的每个 Any 元素在内存中的大小都是一样的” 时,指的是每个元素在数组中占用的内存大小一样 – 它们都是指针,指向存储在堆中的实例。无论这个实例是 Int 还是 String 或者其他的类型。

当然,如果数组元素是协议,是地址引用。

Class & Struct

改变结构体的可变类型的值实际上是重新给结构体变量赋值,拷贝结构体时,如果结构体中有类变量,那么拷贝的是该对象的引用,即不同的结构体可能有着对同一个对象的引用。

weak & unowned 引用

weak 应用作用于可选类型的对象,unowned引用不要求对象可选,但是要确保“被引用者”的生命周期比“引用者”要长。

在对象中,Swift 运行时使用另外一个引用计数来追踪 unowned 引用。当对象没有任何强引用的时候,会释放所有资源 (例如,对其他对象的引用)。然而,只要对象还有 unowned 引用存在,其自身所占用的内存就不会被回收。这块内存会被标记为无效,有时也称作僵尸内存 (zombie memory)。被标记为僵尸内存之后,只要我们尝试访问这个 unowned 引用,就会发生一个运行时错误。

相比弱引用,unowned 引用的开销也小一点,通过它访问属性或调用方法的速度会快一点点。

写时复制

写时复制的意思是,在结构体中的数据,一开始是在多个变量之间共享的:只有在其中一个变量修改了它的数据时,才会产生对数据的复制操作。集合类型(Array、Dictionary、Set、String)都是用写时复制实现的。

如果对结构体的类对象实现写时复制,可以利用isKnownUniquelyReferenced函数,检查一个引用类型的实例是否只有一个所有者。返回false时对引用的对象进行深拷贝,当引用唯一时没有必要进行复制。

Enum

if caseguard case 判断枚举类型是否匹配的同时,可以利用模式匹配定义枚举中包含的值,避免了 switch匹配。

递归

实现一个单向链表:

enum List<Element> {
	case end
	indirect case node(Element, next: List<Element>)
	/// 把一个含有值 `x` 的节点添加到链表的头部。
	/// 然后返回整个链表。
	func cons(_ x: Element) -> List {
		return .node(x, next: self)
	}
}

indirect 告诉编译器把 node 成员表示为一个引用,从而使递归起作用。枚举作为值类型是不能包含自身的,因为如果允许这样的话,在计算类型大小的时候,就会创建一个无限递归。编译器必须能够为每种类型确定一个固定且有限的尺寸。将需要递归的成员作为一个引用是可以解决这个问题的,因为引用类型在其中增加了一个间接层;并且编译器知道任何引用的存储大小总是为 8 个字节 (在一个 64 位的系统上)。当然,如果子节点是数组的话,就不需要indirect了,因为数组内部使用一个引用类型作为存储,已经提供了所需的间接层。

让链表实现 ExpressibleByArrayLiteral 协议,使其能够使用数组字面量来初始化一个链表。在具体的实现中,首先反转作为输入的数组 (因为链表是从结尾开始构建的),然后从 .end 节点开始,使用 reduce 将元素逐个添加到链表中:

extension List: ExpressibleByArrayLiteral {
	public init(arrayLiteral elements: Element...) {
		// reduce 方法接受一个初始的聚合值和一个闭包,每次迭代将当前聚合值和数组中的一个元素作为输入,并返回一个新的聚合值。
		self = elements.reversed().reduce(.end) { partialList, element in
			partialList.cons(element)
		}
	}
}
let list2: List = [3,2,1]
/*
node(3, next: List.node(2, next: List.node(1,
 next: List.end)))
*/

这个链表类型还有一个有趣的特性:它的可持久化。节点都是不可变的 - 一旦创建,你就无法修改它了。添加一个元素到链表中时并不会复制链表;它只是给你一个新的节点,这个节点会链接到现有列表的头部。

固定和非固定枚举

假如在某个版本中switch对一个枚举类型的值穷尽了,但是很有可能在后续版本该枚举类型又增加了,为了防止之前的版本崩溃,我们一般都会加上default的分支。这样做在编译和运行时虽然是没问题的,但是之前的代码就感知不到枚举类型的改动,如果想获取IDE的提示,可以在default加上@unknown的关键词,这样就有在枚举没有穷尽时产生一个warningwitch must be exhaustive

提示和窍门

  • 尽量避免使用嵌套 switch 语句。可以使用元组一次性匹配多个值。
  • 避免用 nonesome 来命名成员。因为在模式匹配的上下文中,它们可能与 Optional 的成员发生冲突。
  • 对那些用保留的关键字来命名的成员使用反引号 (backtick)。如果你使用某些关键字来作为成员名字的话 (例如,default),类型检查器会因为无法解析代码而产生错误。你可以用反引号(``)把名字括起来使用它。
  • 可以像工厂方法一样使用成员。如果一个成员拥有关联值的话,这个枚举值就单独地形成了一个签名为 (AssocValue) -> Enum 的函数。
  • 不要使用关联值来模拟存储属性。获取某个属性时用结构体更为方便,尤其是当枚举的关联值有较多重复时。
  • 把空枚举作为命名空间。除了由模块形成的隐式命名空间之外,Swift 没有内置的命名空间。但我们可以用枚举来“模拟”命名空间。由于类型定义是可以嵌套的,因此外部类型可以充当其包含的所有声明的命名空间。

字符串

基本概念

Character是人类阅读时理解的单个字符,也称为扩展字位簇(extended grapheme cluster),但它可能由多个Unicode标量(Unicode scalars)组成,原因在于字符集的庞大与扩张。由于Unicode是变长编码,一个Unicode标量可能由多个编码单元(code units)组成,对于UTF-8而言,会使用 1~4 个字节编码标量,所以其编码单元为UInt8;对于UTF-16而言,会使用 2/4 个字节编码标量,所以其编码单元为UInt16

// String - UTF-8; NSString - UTF-16
// 调用 count 时,结果都为 7
let single = "Pok\u{00E9}mon" // Pokémon
let double = "Poke\u{0301}mon" // Pokémon

single.unicodeScalars.count // 7
double.unicodeScalars.count // 8

single == double // true
// 比较编码单元速度会更快
single.utf8.elementsEqual(double.utf8) // false

let nssingle = single as NSString
nssingle.length // 7
let nsdouble = double as NSString
nsdouble.length // 8

// 在 UTF-16 编码单元的层面上进行比较,可以用 NSString.compare(_:) 比较组合之后的字面量
nssingle == nsdouble // false

简单的颜文字一般由两个Unicode标量组成,复杂点的颜文字可以通过一个标量值为 U+200D 的不可见零宽连接字符(zero-width joiner,ZWJ)连接简单颜文字而成。例如‍‍‍是由 + ZWJ + + ZWJ + + ZWJ + 构成的。

NSStringString在内存中的视图编码是不同的,在二者之间桥接时会消耗一定的时间和空间。假设通过String初始化了NSMutableAttributedString,获取其string属性时实际上是从NSString属性转化而来的,频繁的该操作会消耗大量时间,但是如果as NSString就能避免这种桥接,这是Swift内部的优化。

字符串索引

做字符串拼接时,字符的数量不等于两个字符串的字符总和,存在相邻字符拼接成新的字位簇的情况。

当使用字符串的索引访问字符时,需要使用String.Index,而不是普通的整数索引。字符和标量并不是一一对应的关系,就算知道给定字符串中第 n 个字符的位置,也并不会对计算这个字符之前有多少个 Unicode 标量有任何帮助。

随机访问字符串并不是O(1)的时间复杂度,查找第n个字符要检查内存中该字符之前所有的字符。而且无法通过下标替换字符,只能用replaceSubrange

字符串类型 Stringindices 属性是一个包含所有字位簇的索引集合。这些索引表示了在字符串中每个字符的开头位置,不论这个字符是由单个 Unicode 标量还是多个 Unicode 标量组成的。

子字符串 SubString 会一直持有整个原始字符串。如果有一个巨大的字符串,它的一个只表示单个字符的子字符串将会在内存中持有整个字符串。即使当原字符串的生命周期本应该结束时,只要子字符串还存在,这部分内存就无法释放。长期存储子字符串实际上会造成内存泄漏,由于原字符串还必须被持有在内存中,但是它们却不能再被访问。

将一个 String 转为 SubString 最快的方式是用不指定任何边界的范围操作符:str[...]

Uninode 标量

Unicode.Scalar.Properties 有着丰富的属性列表。

现在列出字符串中每一个标量的编码点、名称和一般分类只需要对字符串做一点格式化就行了:

"I’m a ‍.".unicodeScalars.map { scalar -> String in
	let codePoint = "U+\(String(scalar.value, radix: 16, uppercase: true))"
	let name = scalar.properties.name ?? "(no name)"
	return "\(codePoint): \(name)\(scalar.properties.generalCategory)"
}.joined(separator: "\n")

Result Builders

@resultBuilder 是 Swift 5.4 引入的一个新特性,主要用于简化构建和处理复杂对象的代码。在 SwiftUI 框架和 Swift 的函数式 API 中已经有它的身影。它可以将一组值经过特定的转换和计算构建为一个结果对象,有点类似于声明式编程的概念。

例如,我们需要创建一个HTML网页,网页包含head标签和body标签,每个标签又可能包含若干子标签。如果没有@resultBuilder,我们需要创建每个标签,设置属性,再将这个标签添加到其父标签下,代码会显得繁琐。

struct HTMLTag: HTMLRepresentable {
    var tag: String
    var children: [HTMLRepresentable] = []

    init(tag: String) {
        self.tag = tag
    }

    init(@HTMLBuilder _ content: () -> HTMLRepresentable...) {
        self.tag = "div"
        self.children = content()
    }

    init(tag: String, @HTMLBuilder _ content: () -> HTMLRepresentable...) {
        self.tag = tag
        self.children = content()
    }
}

@resultBuilder
struct HTMLBuilder {
    static func buildBlock(_ components: HTMLRepresentable...) -> HTMLRepresentable {
        let tag = HTMLTag(tag: "html")
        components.forEach { tag.add(child: $0) }
        return tag
    }
}

func buildPage(@HTMLBuilder content: () -> HTMLRepresentable) -> String {
    let tag = content()
    return tag.htmlString
}

let page = buildPage {
    HTMLTag(tag: "head")
    HTMLTag(tag: "body") {
        HTMLTag(tag: "h1") { "Hello, world!" }
    }
}
print(page)

@HTMLBuilder 可以定义一个或多个如下方法:

  • buildExpression: 处理单个表达式的结果。这通常是处理基本类型,例如 HTML 标签的字符串,或者自定义类型例如 HTMLTag。

  • buildBlock: 把 buildExpression 的所有结果合并成一个结果。例如将多个 HTMLTag 合并为一个。

  • buildOptional: 处理可选表达式(只有 if 一个分支)。例如你有一个返回 HTMLTag? 类型的表达式,这个方法定义了如何处理 nil 。

  • buildEither: 处理条件语句(if/else 分支、switch语句)。例如有一个条件语句,根据不同的条件返回不同的 HTMLTag。

  • buildArray: 处理数组表达式,例如有一个返回 [HTMLTag] 的表达式,这个方法定义了如何处理整个数组。通常是 for…each,每次循环生成一个结果,遍历完成之后将结果收集到一个数组中进行处理。

Concurrency

ConcurrencySwift新引入的一套实现并发的机制。它包括了一系列工具和语言特性,使得在 Swift 中进行异步编程变得更容易。

async/await

Swift中的async/await语法允许你书写看上去像是同步代码的异步代码。一个函数可以被声明为async,这意味着你可以在函数体内使用await关键字。当你 await 一个异步操作的时候,你的函数会被挂起,然后在异步操作结束后再继续执行,从而让其它代码可以在这个异步操作运行时继续执行。

Task/TaskGroup

Swift中的Task是一种可以并发运行的独立工作的抽象,一个Task可以在后台执行一个异步操作。你可以创建一个新的Task来执行一个async闭包。而TaskGroup则是一种可以并发运行许多Task的容器。

AsyncSequence

AsyncSequence是一种异步生成和处理元素序列的方法。它类似于同步的Sequence协议,但其元素是异步产生的。例如,你可以创建一个从文件中逐行读取内容的AsyncSequence,每次循环时,它只会读取和处理一个行,而不需要将整个文件加载到内存中。

func findTitle(url: URL) async throws -> String? {
  for try await line in url.lines {
    if line.contains(""</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> line<span class="token punctuation">.</span><span class="token function">trimmingCharacters</span><span class="token punctuation">(</span><span class="token keyword">in</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>whitespaces<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token constant">nil</span>
<span class="token punctuation">}</span>
</code></pre> 
  <p>对于没有执行顺序的异步代码,可以聚合到一起利用多个线程执行:</p> 
  <pre><code class="prism language-swift"><span class="token keyword">func</span> <span class="token function">findTitlesParallel</span><span class="token punctuation">(</span><span class="token builtin">first</span><span class="token punctuation">:</span> <span class="token constant">URL</span><span class="token punctuation">,</span> second<span class="token punctuation">:</span> <span class="token constant">URL</span><span class="token punctuation">)</span> async <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span>
<span class="token punctuation">(</span><span class="token builtin">String</span><span class="token operator">?</span><span class="token punctuation">,</span>
<span class="token builtin">String</span><span class="token operator">?</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  async <span class="token keyword">let</span> title1 <span class="token operator">=</span> <span class="token function">findTitle</span><span class="token punctuation">(</span>url<span class="token punctuation">:</span> <span class="token builtin">first</span><span class="token punctuation">)</span>   <span class="token comment">// 1</span>
  async <span class="token keyword">let</span> title2 <span class="token operator">=</span> <span class="token function">findTitle</span><span class="token punctuation">(</span>url<span class="token punctuation">:</span> second<span class="token punctuation">)</span>  <span class="token comment">// 2</span>
  <span class="token keyword">let</span> titles <span class="token operator">=</span> <span class="token keyword">try</span> await <span class="token punctuation">[</span>title1<span class="token punctuation">,</span> title2<span class="token punctuation">]</span>    <span class="token comment">// 3</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>titles<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span> titles<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span>              <span class="token comment">// 4</span>
<span class="token punctuation">}</span>
</code></pre> 
  <h4>Actor</h4> 
  <p>Actor是一种保证并发数据安全的模型,它通过串行化在它自身状态上执行的所有操作来提供数据安全性。Actors可以和其它Actors进行通信并对其它Actor进行异步操作,但不能直接访问其它Actors的状态。</p> 
  <p>Swift Actor 中并没有显式地使用锁来保护状态,而是通过内在设计来保证只有一个任务可以同时访问 Actor 的内部状态。这种设计更为友好,因为它避免了通常与加锁和解锁相关的竞态条件和死锁问题。</p> 
  <p>实际上,Actor 模型通过进入队列等待的方式来处理任务。当一个任务想要访问 Actor 的状态或成员函数时,这个任务将会被放入一个队列中,当其他任务完成后,此任务会按照在队列中的顺序被执行。这由编译器和运行时系统来管理,对开发者来说是透明的。</p> 
  <p>因此,也可以将 Actor 看作一个自动处理任务队列和确保数据安全的类,保证了在内部的数据访问和修改是安全的,</p> 
  <p>简单来说,尽管没有显式地锁定 Actor 中的状态,但通过这种内建的排队和任务管理机制,Actor 还是确保了其状态的访问足够安全,一个时刻只有一个任务在访问或修改状态。</p> 
  <pre><code class="prism language-swift">actor <span class="token builtin">Counter</span> <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">var</span> value <span class="token operator">=</span> <span class="token number">0</span>
    
    <span class="token keyword">func</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        value <span class="token operator">+</span><span class="token operator">=</span> <span class="token number">1</span>
        <span class="token function">print</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 使用 Actor</span>
<span class="token keyword">let</span> counter <span class="token operator">=</span> <span class="token function">Counter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token builtin">Task</span> <span class="token punctuation">{</span>
    <span class="token comment">// 需要使用 await 关键字来调用 Actor 中的方法</span>
    await counter<span class="token punctuation">.</span><span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre> 
  <p>Actor 可以实现协议,如果协议的方法要求是串行的,但是属性访问默认都是异步的,此时可以添加<code>nonisolated</code>关键词作<code>actor isolation</code>。</p> 
  <pre><code class="prism language-swift"><span class="token keyword">extension</span> <span class="token builtin">Counter</span><span class="token punctuation">:</span> <span class="token builtin">CustomStringConvertible</span> <span class="token punctuation">{</span>
  nonisolated <span class="token keyword">var</span> description<span class="token punctuation">:</span> <span class="token builtin">String</span> <span class="token punctuation">{</span>
    <span class="token string">"<span class="token interpolation"><span class="token delimiter variable">\(</span>value<span class="token delimiter variable">)</span></span>"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> 
  <h4>Sendable</h4> 
  <p>Sendable 协议是 Swift 中保证数据在并发环境下安全传递的一种手段。要理解这个问题,我们需要明白在并发环境下可能出现的一些问题。</p> 
  <p>当我们在说“传递数据的安全性”时,我们主要是想要避免数据非预期的并发修改。考虑这样的情况:你有两个并发执行的任务A和B。任务A创建了一个数据并送往任务B,当B使用这个数据的同时,A更改了这个数据。这样,B在使用的可能并非是你想要的数据,也许在B看到的数据和A发生变更之间存在微秒级的时间差,就可能导致数据异常或者程序错误。</p> 
  <p>即使在外部通过 Mutex(互斥锁)或者 Actor 来对数据进行保护,这样的问题仍然有可能发生。这是因为 Mutex 或 Actor 主要是保护数据的读者(接收者)和写者(发送者)之间的同步,而非保护同一份数据的多份拷贝。也就是说,即使是 Actor 并不能阻止一份数据在其他地方被修改。</p> 
  <p>为了解决这个问题,我们就要求传递的数据要是 Sendable 协议。如果一个类型符合 Sendable 协议,那么它的实例就可以安全地在多个并发执行的任务中共享。Swift 通过保证值类型在复制时拥有唯一的数据,和限制引用类型在遵守 Sendable 时是只读或者线程安全的,来防止这类问题。</p> 
  <p>举个例子,如果你有一个遵守 Sendable 的结构体,当你在任务A中创建这个结构体的实例并发送给任务B,实际传递给任务B的是这个实例的一份新的复制品,因此任务A中的原始数据修改并不会影响任务B的副本。</p> 
  <p>这就是为什么数据需要符合 Sendable 协议。这为我们在并发环境下处理数据提供了额外的安全性保障。</p> 
  <p>具体来说,Sendable 具有以下安全性保证:</p> 
  <ol> 
   <li> <p>值类型(如 Int,String,Array,等):由于值类型在传递时总是被复制,所以每个使用值的任务都有自己的独立副本,这就消除了数据竞争的可能性。因此,Swift 的许多值类型默认就遵循了 Sendable 协议。</p> </li> 
   <li> <p>引用类型(比如类和闭包):在默认情况下,引用类型是不遵从 Sendable 协议的,因为它们可能有可变状态,如果在无同步措施的情况下在多个任务间共享,可能引发数据竞争。然而,如果一个引用类型是只读的,或者内部状态的访问已经做了同步处理(例如,使用了锁或采用了 actor 模型),那么该类型就可以遵循 Sendable 协议。</p> </li> 
   <li> <p>自定义的符合 Sendable 的类型:可以使用 @Sendable 属性标记自己的自定义类型或函数以表明它们是 Sendable 的。但使用前需要确保其在并发环境下是安全的。</p> </li> 
  </ol> 
  <p>因此,只有遵守 Sendable 协议的数据才能在并发环境中安全地传递,这是因为 Sendable 的设计目标就是保证数据在并发传递过程中的安全性,避免可能的数据竞争。</p> 
 </div> 
</div>��������������������������������������������
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1750228098021867520"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(#,swift,swift,开发语言,ios)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1950221589622616064.htm"
                           title="EasyPlayer播放器系列开发计划2025" target="_blank">EasyPlayer播放器系列开发计划2025</a>
                        <span class="text-muted">xiejiashu</span>
<a class="tag" taget="_blank" href="/search/EasyPlayer/1.htm">EasyPlayer</a><a class="tag" taget="_blank" href="/search/EasyPlayer/1.htm">EasyPlayer</a><a class="tag" taget="_blank" href="/search/EasyPlayer%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">EasyPlayer播放器</a><a class="tag" taget="_blank" href="/search/RTSP%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">RTSP播放器</a><a class="tag" taget="_blank" href="/search/js%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">js播放器</a><a class="tag" taget="_blank" href="/search/Web%E6%92%AD%E6%94%BE%E5%99%A8/1.htm">Web播放器</a>
                        <div>EasyPlayer系列产品发展至今,已经超过10年,从最早的EasyPlayerRTSP播放器,到如今维护的3条线:EasyPlayer-RTSP播放器:Windows、Android、iOS;EasyPlayerPro播放器:Windows、Android、iOS;EasyPlayer.js播放器:H5;这3个播放器各有各的应用场景,用户量也是巨大,像RTSP版本的播放器,到今天依然还有很多低</div>
                    </li>
                    <li><a href="/article/1950170537007312896.htm"
                           title=".net平台的跨平台桌面应用开发的技术方案总结对比" target="_blank">.net平台的跨平台桌面应用开发的技术方案总结对比</a>
                        <span class="text-muted">yuanpan</span>
<a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a>
                        <div>目前,.NET平台提供了多种跨平台桌面应用开发的技术方案,主要包括.NETMAUI、AvaloniaUI、UnoPlatform、Eto.Forms等。以下是它们的核心特点及优缺点对比:1..NETMAUI(.NETMulti-platformAppUI)支持平台:Windows、macOS、iOS、Android核心特点:微软官方维护,继承自Xamarin.Forms,支持XAML和C#开发。提</div>
                    </li>
                    <li><a href="/article/1950158347130957824.htm"
                           title="iOS开发-一个App唤醒另外一个App" target="_blank">iOS开发-一个App唤醒另外一个App</a>
                        <span class="text-muted">善良的皮蛋</span>

                        <div>唤醒App前期准备唤醒App前言:工作中的需求要求在一个App内点击唤醒另外一个App类似很多App可以唤醒淘宝或者京东。1.准备工作1.新建两个App,MyAppOne(触发唤醒的App)、MyAppTow(要唤醒的App)2.iOS9之后要在info.plist中添加白名单,否则无法唤醒。设置白名单LSApplicationQueriesSchemesweixinURLScheme在MyApp</div>
                    </li>
                    <li><a href="/article/1950155535026286592.htm"
                           title="iOS 12.1 tabbar 在pop的时候偏移问题" target="_blank">iOS 12.1 tabbar 在pop的时候偏移问题</a>
                        <span class="text-muted">符大大</span>

                        <div>iOS12.1从bate版到上线版本都存在一个问题,在navigation返回首页的时候,底部tabbar会有一个抖动偏移的现象。苹果真的坑,这种问题存在还敢上线。解决方法:1.新建一个继承UITabbar的的子类,实现以下两个方法2.在tabbarController的viewdidload中添加一下代码。运行之后就解决了12.1的抖动问题了。</div>
                    </li>
                    <li><a href="/article/1950151243905167360.htm"
                           title="(AC)Playlist" target="_blank">(AC)Playlist</a>
                        <span class="text-muted"></span>

                        <div>题目描述Youaregivenaplaylistofaradiostationsinceitsestablishment.Theplaylisthasatotalofnsongs.Whatisthelongestsequenceofsuccessivesongswhereeachsongisunique?输入Thefirstinputlinecontainsanintegern(1≤n≤2*105</div>
                    </li>
                    <li><a href="/article/1950131700457598976.htm"
                           title="【移动端知识】移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现" target="_blank">【移动端知识】移动端多 WebView 互访方案:Android、iOS 与鸿蒙实现</a>
                        <span class="text-muted">问道飞鱼</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E6%8A%80%E6%9C%AF/1.htm">前端开发技术</a><a class="tag" taget="_blank" href="/search/harmonyos/1.htm">harmonyos</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/%E5%A4%9Awebview%E4%BA%92%E8%AE%BF/1.htm">多webview互访</a>
                        <div>移动端多WebView互访方案:Android、iOS与鸿蒙实现移动端多WebView互访方案:Android、iOS与鸿蒙实现一、核心架构设计二、Android平台实现1.基础通信架构2.控制器实现3.WebView安全配置三、iOS平台实现(Swift)1.WKWebView通信桥接2.AppDelegate路由控制四、鸿蒙平台实现(HarmonyOS)1.WebView通信桥接2.Abili</div>
                    </li>
                    <li><a href="/article/1950126282490572800.htm"
                           title="vue项目" target="_blank">vue项目</a>
                        <span class="text-muted">阿什么名字不会重复呢</span>
<a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>vue进行配置创建vue项目vuecreateone//one就是项目名字npmielement-ui--Snpmiaxios--savenpmiecharts--save配置main.jsimportVuefrom'vue'importAppfrom'./App.vue'importrouterfrom'./router'importstorefrom'./store'importElement</div>
                    </li>
                    <li><a href="/article/1949931221622517760.htm"
                           title="因果推断推荐系统工具箱 - PRS(二)" target="_blank">因果推断推荐系统工具箱 - PRS(二)</a>
                        <span class="text-muted">processor4d</span>

                        <div>文章名称【WSDM-2021】【UniversityofVirginia-Google】Non-ClicksMeanIrrelevant?PropensityRatioScoringAsaCorrection核心要点上一节讲解了在unbiasL2R的场景中,基于pairwise比较的损失函数的IPS的方法存在与真实评估指标偏离的问题,这一节讲解如何环节这一问题,并学习模型参数。方法细节问题引入作者</div>
                    </li>
                    <li><a href="/article/1949925685418061824.htm"
                           title="Vue3中Axios的使用-附完整代码" target="_blank">Vue3中Axios的使用-附完整代码</a>
                        <span class="text-muted">【本人】</span>
<a class="tag" taget="_blank" href="/search/Vue/1.htm">Vue</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/1.htm">前端框架</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a>
                        <div>前言首先介绍一下什么是axiosAxios是一个基于promise网络请求库,作用于node.js和浏览器中。它是isomorphic的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.jshttp模块,而在客户端(浏览端)则使用XMLHttpRequests官方网站:Axios中文文档|Axios中文网目前官方最新版本1.8.4一、Axios优势1.简单易用Axio</div>
                    </li>
                    <li><a href="/article/1949920545202040832.htm"
                           title="第一次阶段考试" target="_blank">第一次阶段考试</a>
                        <span class="text-muted">性感迦娜在线吹风丶三百一位</span>

                        <div>1、在11月内,每天早上6点到12点,每隔2小时执行一次/usr/bin/httpd.sh怎么实现?答:006-12/2*11*/bin/bash/usr/bin/httd.sh&>/dev/null2、已知oldboy.txt文件有如下内容:183.250.220.178|-l[20/jul/2017:10:35:14+0800]|POST/audiosearch/searchHTTP/1.1|</div>
                    </li>
                    <li><a href="/article/1949883461095452672.htm"
                           title="音视频流媒体开发【七十四】- WebRTC1-WebRTC入门" target="_blank">音视频流媒体开发【七十四】- WebRTC1-WebRTC入门</a>
                        <span class="text-muted">AlanGe</span>

                        <div>音视频流媒体开发-目录iOS知识点-目录Android-目录Flutter-目录数据结构与算法-目录uni-pp-目录1WebRTC入门1.1什么是WebRTCWebRTC(WebRealTimeCommunication)是Google于2010以6829万美元从GlobalIPSolutions公司购买,并于2011年将其开源,旨在建立一个互联网浏览器间的实时通信的平台,让WebRTC技术成为</div>
                    </li>
                    <li><a href="/article/1949839586070949888.htm"
                           title="TableView嵌套的思路" target="_blank">TableView嵌套的思路</a>
                        <span class="text-muted">senpaiLi</span>

                        <div>在实际代码中,我们iOS的tableView经常会有需要嵌套的情况。一般来说,嵌套时需要控制上层和下层两个scrollView的滑动时机,我这里提供一种思路来实现。(代码是很耦合的,不容易抽离)上层scrollView的滑动手势,下层scrollView会没有反应。这是因为scrollView默认拦截了其他view上面的手势操作,所以要实现如下的方法:/**同时识别多个手势@paramgestur</div>
                    </li>
                    <li><a href="/article/1949822153746608128.htm"
                           title="触摸板增强工具 BetterTouchTool" target="_blank">触摸板增强工具 BetterTouchTool</a>
                        <span class="text-muted"></span>

                        <div>原文地址:BetterTouchToolMac中文触摸板增强工具BetterTouchToolmac版,是一款触摸板增强工具,允许用户使用各种手势来控制其计算机。Bettertouchtoolmac是一个小而高效的macOS应用程序,旨在帮助您为手势定义快捷方式。此外,Bettertouchtool可用于使用常规鼠标和键盘快捷键,并提供伴侣iOS应用程序:您可以使用移动设备来控制计算机。</div>
                    </li>
                    <li><a href="/article/1949817547960414208.htm"
                           title="职场好冰刃 | 重要的微信聊天记录,怎么整理?" target="_blank">职场好冰刃 | 重要的微信聊天记录,怎么整理?</a>
                        <span class="text-muted">Ms想想</span>

                        <div>微信真的有很多自带的好功能,比如微信的自带提醒功能是可以把一些来不及处理的事情按日期、时间提醒你的,而且一旦设置好,还能看到之前完整的聊天内容。但是,聊天记录那么多,手机屏幕那么小,还有没有更好的解决方案?有!以ios系统为例来说明哈(我真的没有安卓手机,对不起!)1.选中任意一条我们想要保留的聊天记录,长按,点击【多选】。2.将所有需要的聊天内容进行勾选。注意:每次只能100条,视频图片需要先加</div>
                    </li>
                    <li><a href="/article/1949816602438463488.htm"
                           title="【大模型LLM学习】function call/agent学习记录" target="_blank">【大模型LLM学习】function call/agent学习记录</a>
                        <span class="text-muted">威化饼的一隅</span>
<a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8BLLM%E5%AD%A6%E4%B9%A0/1.htm">大模型LLM学习</a><a class="tag" taget="_blank" href="/search/agent/1.htm">agent</a><a class="tag" taget="_blank" href="/search/langchain/1.htm">langchain</a><a class="tag" taget="_blank" href="/search/%E6%84%8F%E5%9B%BE%E8%AF%86%E5%88%AB/1.htm">意图识别</a><a class="tag" taget="_blank" href="/search/function/1.htm">function</a><a class="tag" taget="_blank" href="/search/call/1.htm">call</a><a class="tag" taget="_blank" href="/search/%E5%B7%A5%E5%85%B7%E8%B0%83%E7%94%A8/1.htm">工具调用</a>
                        <div>【大模型LLM学习】functioncall/agent学习记录0前言1langchain实现functioncall2调用本地模型3微调本地模型3.1few-shot调用Claude生成Q-A对3.2tools格式3.3agent微调格式3.4swift微调p.s.0前言  记录一下使用langchain做简单的functioncall/agent(或者说意图识别,如果函数有返回值再进行summ</div>
                    </li>
                    <li><a href="/article/1949814878260752384.htm"
                           title="SSM+Vue共享充电宝管理系统 充电宝投放管理系统 共享充电宝维护管理系统Java" target="_blank">SSM+Vue共享充电宝管理系统 充电宝投放管理系统 共享充电宝维护管理系统Java</a>
                        <span class="text-muted">计算机程序老哥</span>

                        <div>作者主页:计算机毕业设计老哥有问题可以主页问我一、开发介绍1.1开发环境开发语言:Java数据库:MySQL系统架构:B/S后端:SSM(Spring+SpringMVC+Mybatis)前端:Vue工具:IDEA或者Eclipse,JDK1.8,Maven二、系统介绍2.1图片展示注册登录页面:登陆前端页面功能:首页、合作商户、充电宝投放、公告栏、个人中心、后台管理首页.png充电宝投放.png</div>
                    </li>
                    <li><a href="/article/1949801227403456512.htm"
                           title="uni-app—— uniapp 苹果手机ios系统中打开doc、xls、pdf、ppt等文件提示文件已过期或被清理" target="_blank">uni-app—— uniapp 苹果手机ios系统中打开doc、xls、pdf、ppt等文件提示文件已过期或被清理</a>
                        <span class="text-muted">dbt@L</span>
<a class="tag" taget="_blank" href="/search/%E5%B0%8F%E6%8A%80%E5%B7%A7/1.htm">小技巧</a><a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a>
                        <div>使用以下即可触发//file是当前文件在线链接downLoadFile(file){uni.showLoading({title:"下载中",})</div>
                    </li>
                    <li><a href="/article/1949785845972135936.htm"
                           title="uni-app小程序证件拍照" target="_blank">uni-app小程序证件拍照</a>
                        <span class="text-muted">胡说先森</span>
<a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>uni-app是一款跨平台的移动应用开发框架,可以用来开发同时运行在多个平台(如iOS、Android、H5、小程序等)的应用。在uni-app中,可以使用以下方式进行证件拍照:使用uni.chooseImageAPI调用系统相机拍照:uni.chooseImage({count:1,//最多可以选择的图片张数,默认9sizeType:['original','compre</div>
                    </li>
                    <li><a href="/article/1949777736079831040.htm"
                           title="音视频流媒体开发【七十二】- RTSP流媒体7-SDP协议分析" target="_blank">音视频流媒体开发【七十二】- RTSP流媒体7-SDP协议分析</a>
                        <span class="text-muted">AlanGe</span>

                        <div>音视频流媒体开发-目录iOS知识点-目录Android-目录Flutter-目录数据结构与算法-目录uni-pp-目录SDP(SessionDescriptionProtocol)完全是⼀种会话描述格式―它不属于传输协议―它只使⽤不同的适当的传输协议,包括会话通知协议(SAP)、会话初始协议(SIP)、实时流协议(RTSP)、MIME扩展协议的电⼦邮件以及超⽂本传输协议(HTTP)。SDP协议是也</div>
                    </li>
                    <li><a href="/article/1949713345170960384.htm"
                           title="我们在业余时间做了一款优雅的节气小组件 App,希望它能成为你桌面上的一抹东方诗意" target="_blank">我们在业余时间做了一款优雅的节气小组件 App,希望它能成为你桌面上的一抹东方诗意</a>
                        <span class="text-muted"></span>

                        <div>我们在业余时间做了一款优雅的节气小组件App,希望它能成为你桌面上的一抹东方诗意我们是一支热爱设计与东方文化的小团队,这款名为「二十四节气」的iOS应用,是我们在工作之余,利用碎片时间,一点一滴打磨出来的作品。这个App没有商业预算,也不是“下一个爆款”那种级别的产品,但我们希望它足够克制、安静、有韵味——它不依赖算法,不依赖打扰式推送,只靠节气本身的美与节奏,来提醒我们:春生、夏长、秋收、冬藏,</div>
                    </li>
                    <li><a href="/article/1949705772782907392.htm"
                           title="深入详解K近邻算法(KNN):基本概念、原理及在医学影像领域的应用与实现" target="_blank">深入详解K近邻算法(KNN):基本概念、原理及在医学影像领域的应用与实现</a>
                        <span class="text-muted">猿享天开</span>
<a class="tag" taget="_blank" href="/search/%E8%BF%91%E9%82%BB%E7%AE%97%E6%B3%95/1.htm">近邻算法</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%8C%BB%E5%AD%A6%E5%BD%B1%E5%83%8F/1.htm">医学影像</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">大模型</a>
                        <div>博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++,C#,Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQLserver,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,</div>
                    </li>
                    <li><a href="/article/1949705773940535296.htm"
                           title="机器学习模型评估:交叉验证、混淆矩阵、ROC曲线及其在医学影像领域的应用" target="_blank">机器学习模型评估:交叉验证、混淆矩阵、ROC曲线及其在医学影像领域的应用</a>
                        <span class="text-muted">猿享天开</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E7%9F%A9%E9%98%B5/1.htm">矩阵</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/DICOM%E5%8C%BB%E5%AD%A6%E5%BD%B1%E5%83%8F/1.htm">DICOM医学影像</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E5%9E%8B%E8%AF%84%E4%BC%B0/1.htm">模型评估</a>
                        <div>博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++,C#,Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQLserver,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,</div>
                    </li>
                    <li><a href="/article/1949685517893300224.htm"
                           title="iOS渠道跟踪 3中方案的利弊(干货)" target="_blank">iOS渠道跟踪 3中方案的利弊(干货)</a>
                        <span class="text-muted">邂逅阳光</span>

                        <div>总的来说有三种方案(1、2点算一条)1、使用SFSafariViewController来获取safari内的cookie,后来发现ios11之后是获取不到的,这一方案只适用于(ios9~ios11以下)2、ios11之后SFAuthenticationSession代替了SFSafariViewController共享cookie,但SFAuthenticationSession是OC语言的一个</div>
                    </li>
                    <li><a href="/article/1949678616254148608.htm"
                           title="iOS 解决 veiw 之间 因为父view alpha 等于0 导致全部透明的问题" target="_blank">iOS 解决 veiw 之间 因为父view alpha 等于0 导致全部透明的问题</a>
                        <span class="text-muted">村雨灬龑</span>

                        <div>首先先看一段代码,矛盾的起因,因为父view的alpha=0,而又想让子view能够显示出来,不受父view的alpha=0的影响。UIView*view=[[UIViewalloc]init];view.backgroundColor=[UIColorredColor];//因为下面的label没有显示是因为父view的alpha=0view.alpha=0;view.frame=CGRectM</div>
                    </li>
                    <li><a href="/article/1949678033686294528.htm"
                           title="在 Vue 3 中使用 axios 拦截器处理过期 token 时报错的问题" target="_blank">在 Vue 3 中使用 axios 拦截器处理过期 token 时报错的问题</a>
                        <span class="text-muted"></span>

                        <div>使用axios拦截器处理过期token的代码如下所示axios.interceptors.response.use(function(response){console.log('返回结果:',response);returnresponse;},error=>{console.log('返回错误:',error);constresponse=error.response;conststatus=</div>
                    </li>
                    <li><a href="/article/1949639714713694208.htm"
                           title="Ubuntu服务器安装与运维手册——操作纯享版" target="_blank">Ubuntu服务器安装与运维手册——操作纯享版</a>
                        <span class="text-muted">夏天里的肥宅水</span>
<a class="tag" taget="_blank" href="/search/LINUX/1.htm">LINUX</a><a class="tag" taget="_blank" href="/search/SQL/1.htm">SQL</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a>
                        <div>本手册汇总了从硬件预配置、Ubuntu安装、网络与服务配置,到Windows/macOS访问共享、MySQL初始化的完整流程,便于今后运维参考。目录环境与硬件概览BIOS/UEFI设置制作与启动安装介质Ubuntu24.04LTS安装流程静态IP配置(netplan)SSH远程登录配置Samba文件共享配置MySQL数据库初始化FTPvsSamba对比常见问题&解决环境与硬件概览机型:DellXP</div>
                    </li>
                    <li><a href="/article/1949632154111635456.htm"
                           title="dart和python哪个好学_最近学习 Dart 语言,分享一下心得 (入门级)" target="_blank">dart和python哪个好学_最近学习 Dart 语言,分享一下心得 (入门级)</a>
                        <span class="text-muted"></span>

                        <div>每隔一段时间,总有一种新(或较新)的语言进入大家的视线,比如Kotlin,Rust,Go等,我查了一下它们的“出生日期”:Rust(2011自举,2015v1.0)Go(2012,v1.0)Kotlin(2012开源,2016v1.0)TypeScript(2014,v1.0)Swift(2014,v1.0)Dart/Flutter(2018,Dartv2.0)由于Dart1实质上已经被放弃,Da</div>
                    </li>
                    <li><a href="/article/1949618407762620416.htm"
                           title="Swift 5, TypeScript, and Python Async/Await 机制对比分析" target="_blank">Swift 5, TypeScript, and Python Async/Await 机制对比分析</a>
                        <span class="text-muted">步子哥</span>
<a class="tag" taget="_blank" href="/search/swift/1.htm">swift</a><a class="tag" taget="_blank" href="/search/typescript/1.htm">typescript</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                        <div>Swift5、TypeScript和Python的async/await机制都旨在简化异步编程,但它们在实现细节、并发模型和性能特征上有所不同。Swift5的async/await构建于结构化并发之上,强调通过Task进行非阻塞的任务挂起和高效的线程利用。TypeScript(JavaScript)的async/await是基于Promise和事件循环的语法糖,适用于单线程非阻塞I/O环境。Pyt</div>
                    </li>
                    <li><a href="/article/1949603273870733312.htm"
                           title="iOS——消息传递和消息转发机制" target="_blank">iOS——消息传递和消息转发机制</a>
                        <span class="text-muted">Yeppppppppp</span>
<a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/objective-c/1.htm">objective-c</a><a class="tag" taget="_blank" href="/search/xcode/1.htm">xcode</a>
                        <div>iOS——消息传递和消息转发机制消息传递机制选择子SEL、IMP、Method选择子SELIMPMethod执行过程消息发送阶段动态解析阶段动态解析测试消息转发阶段消息转发测试总结消息传递机制在学习消息传递机制是如何实现之前,有几个基础知识我们必须要理解:在OC里使用对象调用方法十分普遍,用OC的术语来说调用对象的方法就是给某个对象发送某条消息简单的来说就是我们去调用方法编译器告诉某个对象你该执行</div>
                    </li>
                    <li><a href="/article/1949602642778976256.htm"
                           title="Hololens与iOS设备间的第三视角DLL实现指南" target="_blank">Hololens与iOS设备间的第三视角DLL实现指南</a>
                        <span class="text-muted">小馬锅</span>

                        <div>本文还有配套的精品资源,点击获取简介:通过“hololens+ios第三视角dll”这一技术方案,开发者可以实现在Hololens全息眼镜与iOS设备间共享第三视角的功能。该方案要求使用特定的动态链接库(DLL)文件,以支持设备间的通信与视频流处理。Specta可能是该方案的关键组件或库的名称,它可能包含视频流传输、设备连接和交互的相关APIs。为了成功实现这一功能,开发者需要深入理解Holole</div>
                    </li>
                                <li><a href="/article/101.htm"
                                       title="数据采集高并发的架构应用" target="_blank">数据采集高并发的架构应用</a>
                                    <span class="text-muted">3golden</span>
<a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a>
                                    <div>问题的出发点: 
 
         最近公司为了发展需要,要扩大对用户的信息采集,每个用户的采集量估计约2W。如果用户量增加的话,将会大量照成采集量成3W倍的增长,但是又要满足日常业务需要,特别是指令要及时得到响应的频率次数远大于预期。 
      &n</div>
                                </li>
                                <li><a href="/article/228.htm"
                                       title="不停止 MySQL 服务增加从库的两种方式" target="_blank">不停止 MySQL 服务增加从库的两种方式</a>
                                    <span class="text-muted">brotherlamp</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/linux%E8%A7%86%E9%A2%91/1.htm">linux视频</a><a class="tag" taget="_blank" href="/search/linux%E8%B5%84%E6%96%99/1.htm">linux资料</a><a class="tag" taget="_blank" href="/search/linux%E6%95%99%E7%A8%8B/1.htm">linux教程</a><a class="tag" taget="_blank" href="/search/linux%E8%87%AA%E5%AD%A6/1.htm">linux自学</a>
                                    <div>现在生产环境MySQL数据库是一主一从,由于业务量访问不断增大,故再增加一台从库。前提是不能影响线上业务使用,也就是说不能重启MySQL服务,为了避免出现其他情况,选择在网站访问量低峰期时间段操作。 
 一般在线增加从库有两种方式,一种是通过mysqldump备份主库,恢复到从库,mysqldump是逻辑备份,数据量大时,备份速度会很慢,锁表的时间也会很长。另一种是通过xtrabacku</div>
                                </li>
                                <li><a href="/article/355.htm"
                                       title="Quartz——SimpleTrigger触发器" target="_blank">Quartz——SimpleTrigger触发器</a>
                                    <span class="text-muted">eksliang</span>
<a class="tag" taget="_blank" href="/search/SimpleTrigger/1.htm">SimpleTrigger</a><a class="tag" taget="_blank" href="/search/TriggerUtils/1.htm">TriggerUtils</a><a class="tag" taget="_blank" href="/search/quartz/1.htm">quartz</a>
                                    <div>转载请出自出处:http://eksliang.iteye.com/blog/2208166 一.概述 
SimpleTrigger触发器,当且仅需触发一次或者以固定时间间隔周期触发执行; 
  二.SimpleTrigger的构造函数 
 
 SimpleTrigger(String name, String group):通过该构造函数指定Trigger所属组和名称; 
 Simpl</div>
                                </li>
                                <li><a href="/article/482.htm"
                                       title="Informatica应用(1)" target="_blank">Informatica应用(1)</a>
                                    <span class="text-muted">18289753290</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/workflow/1.htm">workflow</a><a class="tag" taget="_blank" href="/search/lookup/1.htm">lookup</a><a class="tag" taget="_blank" href="/search/%E7%BB%84%E4%BB%B6/1.htm">组件</a><a class="tag" taget="_blank" href="/search/Informatica/1.htm">Informatica</a>
                                    <div>1.如果要在workflow中调用shell脚本有一个command组件,在里面设置shell的路径;调度wf可以右键出现schedule,现在用的是HP的tidal调度wf的执行。 
2.designer里面的router类似于SSIS中的broadcast(多播组件);Reset_Workflow_Var:参数重置 (比如说我这个参数初始是1在workflow跑得过程中变成了3我要在结束时还要</div>
                                </li>
                                <li><a href="/article/609.htm"
                                       title="python 获取图片验证码中文字" target="_blank">python 获取图片验证码中文字</a>
                                    <span class="text-muted">酷的飞上天空</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a>
                                    <div>根据现成的开源项目 http://code.google.com/p/pytesser/改写 
在window上用easy_install安装不上  看了下源码发现代码很少  于是就想自己改写一下 
  
添加支持网络图片的直接解析 
  
  
#coding:utf-8 
#import sys 
#reload(sys) 
#sys.s</div>
                                </li>
                                <li><a href="/article/736.htm"
                                       title="AJAX" target="_blank">AJAX</a>
                                    <span class="text-muted">永夜-极光</span>
<a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a>
                                    <div>1.AJAX功能:动态更新页面,减少流量消耗,减轻服务器负担 
  
2.代码结构: 
   
<html>

<head>
<script type="text/javascript">
function loadXMLDoc()
{
.... AJAX script goes here ...
</div>
                                </li>
                                <li><a href="/article/863.htm"
                                       title="创业OR读研" target="_blank">创业OR读研</a>
                                    <span class="text-muted">随便小屋</span>
<a class="tag" taget="_blank" href="/search/%E5%88%9B%E4%B8%9A/1.htm">创业</a>
                                    <div>        现在研一,有种想创业的想法,不知道该不该去实施。因为对于的我情况这两者是矛盾的,可能就是鱼与熊掌不能兼得。 
       
        研一的生活刚刚过去两个月,我们学校主要的是</div>
                                </li>
                                <li><a href="/article/990.htm"
                                       title="需求做得好与坏直接关系着程序员生活质量" target="_blank">需求做得好与坏直接关系着程序员生活质量</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/IT+%E7%94%9F%E6%B4%BB/1.htm">IT 生活</a>
                                    <div>  
        这个故事还得从去年换工作的事情说起,由于自己不太喜欢第一家公司的环境我选择了换一份工作。去年九月份我入职现在的这家公司,专门从事金融业内软件的开发。十一月份我们整个项目组前往北京做现场开发,从此苦逼的日子开始了。 
       系统背景:五月份就有同事前往甲方了解需求一直到6月份,后续几个月也完</div>
                                </li>
                                <li><a href="/article/1117.htm"
                                       title="如何定义和区分高级软件开发工程师" target="_blank">如何定义和区分高级软件开发工程师</a>
                                    <span class="text-muted">aoyouzi</span>

                                    <div>在软件开发领域,高级开发工程师通常是指那些编写代码超过 3 年的人。这些人可能会被放到领导的位置,但经常会产生非常糟糕的结果。Matt Briggs 是一名高级开发工程师兼 Scrum 管理员。他认为,单纯使用年限来划分开发人员存在问题,两个同样具有 10 年开发经验的开发人员可能大不相同。近日,他发表了一篇博文,根据开发者所能发挥的作用划分软件开发工程师的成长阶段。 
  初</div>
                                </li>
                                <li><a href="/article/1244.htm"
                                       title="Servlet的请求与响应" target="_blank">Servlet的请求与响应</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/servlet/1.htm">servlet</a><a class="tag" taget="_blank" href="/search/get%E6%8F%90%E4%BA%A4/1.htm">get提交</a><a class="tag" taget="_blank" href="/search/java%E5%A4%84%E7%90%86post%E6%8F%90%E4%BA%A4/1.htm">java处理post提交</a>
                                    <div>  
Servlet是tomcat中的一个重要组成,也是负责客户端和服务端的中介 
  
  
1,Http的请求方式(get  ,post); 
  客户端的请求一般都会都是Servlet来接受的,在接收之前怎么来确定是那种方式提交的,以及如何反馈,Servlet中有相应的方法,  http的get方式 servlet就是都doGet(</div>
                                </li>
                                <li><a href="/article/1371.htm"
                                       title="web.xml配置详解之listener" target="_blank">web.xml配置详解之listener</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/web.xml/1.htm">web.xml</a><a class="tag" taget="_blank" href="/search/listener/1.htm">listener</a>
                                    <div>一.定义 
<listener>  
	<listen-class>com.myapp.MyListener</listen-class>  
</listener> 
  
二.作用        该元素用来注册一个监听器类。可以收到事件什么时候发生以及用什么作为响</div>
                                </li>
                                <li><a href="/article/1498.htm"
                                       title="Web页面性能优化(yahoo技术)" target="_blank">Web页面性能优化(yahoo技术)</a>
                                    <span class="text-muted">Bill_chen</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/Yahoo/1.htm">Yahoo</a>
                                    <div>1.尽可能的减少HTTP请求数 content 
2.使用CDN server 
3.添加Expires头(或者 Cache-control) server 
4.Gzip 组件 server 
5.把CSS样式放在页面的上方。 css 
6.将脚本放在底部(包括内联的) javascript 
7.避免在CSS中使用Expressions css 
8.将javascript和css独立成外部文</div>
                                </li>
                                <li><a href="/article/1625.htm"
                                       title="【MongoDB学习笔记八】MongoDB游标、分页查询、查询结果排序" target="_blank">【MongoDB学习笔记八】MongoDB游标、分页查询、查询结果排序</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a>
                                    <div>游标 
  
游标,简单的说就是一个查询结果的指针。游标作为数据库的一个对象,使用它是包括 
 
 声明 
 打开 
 循环抓去一定数目的文档直到结果集中的所有文档已经抓取完 
 关闭游标 
 
  
游标的基本用法,类似于JDBC的ResultSet(hasNext判断是否抓去完,next移动游标到下一条文档),在获取一个文档集时,可以提供一个类似JDBC的FetchSize</div>
                                </li>
                                <li><a href="/article/1752.htm"
                                       title="ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法" target="_blank">ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 的解决方法</a>
                                    <span class="text-muted">白糖_</span>
<a class="tag" taget="_blank" href="/search/ORA-12514/1.htm">ORA-12514</a>
                                    <div> 今天通过Oracle SQL*Plus连接远端服务器的时候提示“监听程序当前无法识别连接描述符中请求服务”,遂在网上找到了解决方案: 
 ①打开Oracle服务器安装目录\NETWORK\ADMIN\listener.ora文件,你会看到如下信息: 
  
# listener.ora Network Configuration File: D:\database\Oracle\net</div>
                                </li>
                                <li><a href="/article/1879.htm"
                                       title="Eclipse 问题 A resource exists with a different case" target="_blank">Eclipse 问题 A resource exists with a different case</a>
                                    <span class="text-muted">bozch</span>
<a class="tag" taget="_blank" href="/search/eclipse/1.htm">eclipse</a>
                                    <div>在使用Eclipse进行开发的时候,出现了如下的问题: 
Description Resource Path Location TypeThe project was not built due to "A resource exists with a different case: '/SeenTaoImp_zhV2/bin/seentao'.&</div>
                                </li>
                                <li><a href="/article/2006.htm"
                                       title="编程之美-小飞的电梯调度算法" target="_blank">编程之美-小飞的电梯调度算法</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E/1.htm">编程之美</a>
                                    <div>

public class AptElevator {

	/**
	 * 编程之美 小飞 电梯调度算法
	 * 在繁忙的时间,每次电梯从一层往上走时,我们只允许电梯停在其中的某一层。
	 * 所有乘客都从一楼上电梯,到达某层楼后,电梯听下来,所有乘客再从这里爬楼梯到自己的目的层。
	 * 在一楼时,每个乘客选择自己的目的层,电梯则自动计算出应停的楼层。
	 * 问:电梯停在哪</div>
                                </li>
                                <li><a href="/article/2133.htm"
                                       title="SQL注入相关概念" target="_blank">SQL注入相关概念</a>
                                    <span class="text-muted">chenbowen00</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a>
                                    <div>SQL Injection:就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。 
 
具体来说,它是利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。 
 
首先让我们了解什么时候可能发生SQ</div>
                                </li>
                                <li><a href="/article/2260.htm"
                                       title="[光与电]光子信号战防御原理" target="_blank">[光与电]光子信号战防御原理</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E5%8E%9F%E7%90%86/1.htm">原理</a>
                                    <div> 
 
      无论是在战场上,还是在后方,敌人都有可能用光子信号对人体进行控制和攻击,那么采取什么样的防御方法,最简单,最有效呢? 
 
      我们这里有几个山寨的办法,可能有些作用,大家如果有兴趣可以去实验一下 
 
      根据光</div>
                                </li>
                                <li><a href="/article/2387.htm"
                                       title="oracle 11g新特性:Pending Statistics" target="_blank">oracle 11g新特性:Pending Statistics</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/dbms_stats/1.htm">dbms_stats</a>
                                    <div>oracle 11g新特性:Pending Statistics 转 
 
从11g开始,表与索引的统计信息收集完毕后,可以选择收集的统信息立即发布,也可以选择使新收集的统计信息处于pending状态,待确定处于pending状态的统计信息是安全的,再使处于pending状态的统计信息发布,这样就会避免一些因为收集统计信息立即发布而导致SQL执行计划走错的灾难。 
 
在 11g 之前的版本中,D</div>
                                </li>
                                <li><a href="/article/2514.htm"
                                       title="快速理解RequireJs" target="_blank">快速理解RequireJs</a>
                                    <span class="text-muted">dengkane</span>
<a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/requirejs/1.htm">requirejs</a>
                                    <div>RequireJs已经流行很久了,我们在项目中也打算使用它。它提供了以下功能: 
 
 声明不同js文件之间的依赖 
 可以按需、并行、延时载入js库 
 可以让我们的代码以模块化的方式组织 
 
初看起来并不复杂。 在html中引入requirejs 
在HTML中,添加这样的 <script> 标签: 
<script src="/path/to</div>
                                </li>
                                <li><a href="/article/2641.htm"
                                       title="C语言学习四流程控制if条件选择、for循环和强制类型转换" target="_blank">C语言学习四流程控制if条件选择、for循环和强制类型转换</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/c/1.htm">c</a>
                                    <div># include <stdio.h>

int main(void)
{
	int i, j;

	scanf("%d %d", &i, &j);

	if (i > j)
		printf("i大于j\n");
	else
		printf("i小于j\n");

	retu</div>
                                </li>
                                <li><a href="/article/2768.htm"
                                       title="dictionary的使用要注意" target="_blank">dictionary的使用要注意</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/IO/1.htm">IO</a>
                                    <div>NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                          user.user_id , @"id",
                          user.username , @"username",
         </div>
                                </li>
                                <li><a href="/article/2895.htm"
                                       title="Android 中的资源访问(Resource)" target="_blank">Android 中的资源访问(Resource)</a>
                                    <span class="text-muted">finally_m</span>
<a class="tag" taget="_blank" href="/search/xml/1.htm">xml</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/String/1.htm">String</a><a class="tag" taget="_blank" href="/search/drawable/1.htm">drawable</a><a class="tag" taget="_blank" href="/search/color/1.htm">color</a>
                                    <div> 
简单的说,Android中的资源是指非代码部分。例如,在我们的Android程序中要使用一些图片来设置界面,要使用一些音频文件来设置铃声,要使用一些动画来显示特效,要使用一些字符串来显示提示信息。那么,这些图片、音频、动画和字符串等叫做Android中的资源文件。 
在Eclipse创建的工程中,我们可以看到res和assets两个文件夹,是用来保存资源文件的,在assets中保存的一般是原生</div>
                                </li>
                                <li><a href="/article/3022.htm"
                                       title="Spring使用Cache、整合Ehcache" target="_blank">Spring使用Cache、整合Ehcache</a>
                                    <span class="text-muted">234390216</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/cache/1.htm">cache</a><a class="tag" taget="_blank" href="/search/ehcache/1.htm">ehcache</a><a class="tag" taget="_blank" href="/search/%40Cacheable/1.htm">@Cacheable</a>
                                    <div>Spring使用Cache 
  
  
       从3.1开始,Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的</div>
                                </li>
                                <li><a href="/article/3149.htm"
                                       title="当druid遇上oracle blob(clob)" target="_blank">当druid遇上oracle blob(clob)</a>
                                    <span class="text-muted">jackyrong</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a>
                                    <div>http://blog.csdn.net/renfufei/article/details/44887371 
 
众所周知,Oracle有很多坑, 所以才有了去IOE。 
 
在使用Druid做数据库连接池后,其实偶尔也会碰到小坑,这就是使用开源项目所必须去填平的。【如果使用不开源的产品,那就不是坑,而是陷阱了,你都不知道怎么去填坑】 
 
用Druid连接池,通过JDBC往Oracle数据库的</div>
                                </li>
                                <li><a href="/article/3276.htm"
                                       title="easyui datagrid pagination获得分页页码、总页数等信息" target="_blank">easyui datagrid pagination获得分页页码、总页数等信息</a>
                                    <span class="text-muted">ldzyz007</span>

                                    <div>var grid = $('#datagrid');  
var options = grid.datagrid('getPager').data("pagination").options;  
var curr = options.pageNumber;  
var total = options.total;  
var max =</div>
                                </li>
                                <li><a href="/article/3403.htm"
                                       title="浅析awk里的数组" target="_blank">浅析awk里的数组</a>
                                    <span class="text-muted">nigelzeng</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84/1.htm">二维数组</a><a class="tag" taget="_blank" href="/search/array/1.htm">array</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a><a class="tag" taget="_blank" href="/search/awk/1.htm">awk</a>
                                    <div>awk绝对是文本处理中的神器,它本身也是一门编程语言,还有许多功能本人没有使用到。这篇文章就单单针对awk里的数组来进行讨论,如何利用数组来帮助完成文本分析。 
  
有这么一组数据: 
   abcd,91#31#2012-12-31 11:24:00 
case_a,136#19#2012-12-31 11:24:00 
case_a,136#23#2012-12-31 1</div>
                                </li>
                                <li><a href="/article/3530.htm"
                                       title="搭建 CentOS 6 服务器(6) - TigerVNC" target="_blank">搭建 CentOS 6 服务器(6) - TigerVNC</a>
                                    <span class="text-muted">rensanning</span>
<a class="tag" taget="_blank" href="/search/centos/1.htm">centos</a>
                                    <div>安装GNOME桌面环境 
 
# yum groupinstall "X Window System" "Desktop" 
 
 
安装TigerVNC 
 
# yum -y install tigervnc-server tigervnc 
 
 
启动VNC服务 
 
# /etc/init.d/vncserver restart
# vncser</div>
                                </li>
                                <li><a href="/article/3657.htm"
                                       title="Spring 数据库连接整理" target="_blank">Spring 数据库连接整理</a>
                                    <span class="text-muted">tomcat_oracle</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/bean/1.htm">bean</a><a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a>
                                    <div>1、数据库连接jdbc.properties配置详解    jdbc.url=jdbc:hsqldb:hsql://localhost/xdb     jdbc.username=sa     jdbc.password=     jdbc.driver=不同的数据库厂商驱动,此处不一一列举     接下来,详细配置代码如下:      
Spring连接池      </div>
                                </li>
                                <li><a href="/article/3784.htm"
                                       title="Dom4J解析使用xpath java.lang.NoClassDefFoundError: org/jaxen/JaxenException异常" target="_blank">Dom4J解析使用xpath java.lang.NoClassDefFoundError: org/jaxen/JaxenException异常</a>
                                    <span class="text-muted">xp9802</span>

                                    <div>用Dom4J解析xml,以前没注意,今天使用dom4j包解析xml时在xpath使用处报错 
     异常栈:java.lang.NoClassDefFoundError: org/jaxen/JaxenException异常  
     导入包 jaxen-1.1-beta-6.jar 解决; 
&nb</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>