- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章iOS 中weak的实现代码示例由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
只要学过 ios 的人,都会对 strong、weak、copy等关键字应该都会很熟悉。weak 属性关键字就是弱引用,它不会增加引用计数但却能保证指针的安全访问,在对象释放后置为 nil,从而避免错误的内存访问。主要为了解决循环引用的问题.
接下来,我们会从 objc 库中的 nsobject.mm、 objc-weak.h 以及 objc-weak.mm 文件出发,去具体了解 weak 的实现过程.
weak 的内部结构 。
runtime 维护了一个weak表,用于存储指向某个对象的所有weak指针。weak 表是由单个自旋锁管理的散列表。 weak表其实是一个hash表,key 是所指对象的指针,value是weak指针的地址(这个地址的值是所指向对象的地址)数组.
在下面涉及的源码中,我们会看到以下几个类型:
sidetable、weak_table_t、weak_entry_t 这几个结构体.
1
2
3
4
5
6
7
8
9
10
|
struct
sidetable {
// 自旋锁,用来保证线程安全
spinlock_t slock;
// 引用计数表
refcountmap refcnts;
// weak 表
weak_table_t weak_table;
...
};
|
sidetable,它用来管理引用计数表和 weak 表,并使用 spinlock_lock 自旋锁来防止操作表结构时可能的竞态条件。它用一个 64*128 大小的uint8_t 静态数组作为 buffer 来保存所有的 sidetable 实例。这个结构体里面包含三个变量,第一个spinlock_t,它是一个自旋锁,用来保证线程安全。第二个refcountmap,是引用计数表,每个对象的引用计数保存在全局的引用计数表中,一个对象地址对应一个引用计数。第三个就是我们接下来要讲的 weak 表,所有的 weak 变量会被加入到全局的weak表中,表的 key 是 weak 修饰的变量指向的对象, value 值就是 weak 修饰的变量。接下来,我们具体看看这个 weak 表 。
1
2
3
4
5
6
7
8
9
10
|
struct
weak_table_t {
// 保存了所有指向指定对象的 weak 指针
weak_entry_t *weak_entries;
// 存储空间,即 entries 的数目
size_t
num_entries;
// 参与判断引用计数辅助量
uintptr_t
mask;
// hash key 最大偏移量
uintptr_t
max_hash_displacement;
};
|
这个是全局弱引用的 hash 表。它的作用就是在对象执行 dealloc 的时候将所有指向该对象的 weak 指针的值设为 nil, 避免悬空指针。它使用不定类型对象的地址的 hash 化后的数值作为 key,用 weak_entry_t 类型的结构体对象作为 value。其中 weak_entry_t 是存储在弱引用表中的一个内部结构体,它负责维护和存储指向一个对象的所有弱引用 hash 表。其定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 存储在弱引用表中的一个内部结构体
#define weak_inline_count 4
struct
weak_entry_t {
disguisedptr<objc_object> referent;
// 封装 objc_object 指针,即 weak 修饰的变量指向的对象
union
{
struct
{
weak_referrer_t *referrers;
uintptr_t
out_of_line : 1;
// lsb 最低有效元 当标志位为0时,增加引用表指针纬度,
// 当其为0的时候, weak_referrer_t 成员将扩展为静态数组型的 hash table
uintptr_t
num_refs : ptr_minus_1;
// 引用数值,这里记录弱引用表中引用有效数字,即里面元素的数量
uintptr_t
mask;
uintptr_t
max_hash_displacement;
// hash 元素上限阀值
};
struct
{
// out_of_line=0 is lsb of one of these (don't care which)
weak_referrer_t inline_referrers[weak_inline_count];
};
};
};
|
在 weak_entry_t 的结构中, disguisedptr<objc_object> 是对 objc_object * 指针及其一些操作进行的封装,目的就是为了让它给人看起来不会有内存泄露的样子,其内容可以理解为对象的内存地址。out_of-line 成员为最低有效位,当其为 0 的时候,weak_referrer_t 成员将扩展为一个静态数组型的 hash table。其实 weak_referrer 是objc_objcet 的别名,定义如下:typedef objc_object ** weak_referrer_t,
它通过一个二维指针地址偏移,用下标作为 hash 的 key,做成了一个弱引用散列.
每个对象的 sidetable 中的 weak_table_t 都是全局 weak 表的入口,以引用计数对象为键找到其所记录的 weak 修饰的对象。weak_entry_t 中的 referrers 有两种形式,当 out_of_line 为 0 的时候,referrers 是一个静态数组型的表,数组大小默认为 weak_inline_count 大小,当 out_of_line 不为 0 的时候,referrers 是一个动态数组,内容随之增加.
weak 实现原理的过程 。
当我们用 weak 修饰属性的时候,它是怎么实现当所引用的对象被废弃的时候,变量置为 nil,我们来探究一下.
1
2
3
4
|
{
id obj1 = [[nsobject alloc] init];
id __weak obj2 = obj1;
}
|
经过编译期转换之后,以上代码会变成下面这样 。
id obj2; objc_initweak(&obj2, obj1); objc_destroyweak(&obj2),
我们发现,weak 修饰符变量是通过 objc_initweak 函数来初始化的,在变量作用域结束的时候通过 objc_destroyweak 函数来释放该变量的。接下来,我们看看这两个函数的源码.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
id objc_initweak(id *location, id newobj)
{
// 查看对象实例是否有效
// 无效对象直接导致指针释放
if
(!newobj) {
*location = nil;
return
nil;
}
// 这里传递了三个 bool 数值
// 使用 template 进行常量参数传递是为了优化性能
return
storeweak<
false
/*old*/
,
true
/*new*/
,
true
/*crash*/
>
(location, (objc_object*)newobj);
}
|
1
2
3
4
5
|
void
objc_destroyweak(id *location)
{
(
void
)storeweak<
true
/*old*/
,
false
/*new*/
,
false
/*crash*/
>
(location, nil);
}
|
对这两个方法的分析后,我们发现它们都调用了storeweak 这个函数,但是两个方法传入的参数却稍有不同.
init 方法中,第一个参数为 weak 修饰的变量,第二个参数为引用计数对象。但在 destoryweak 函数,第一参数依旧为 weak 修饰的变量,第二个参数为 nil。那这块传入不同的参数到底代表什么,我们继续分析 storeweak 这个函数.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
// 更新一个弱引用变量
// 如果 haveold 是 true, 变量是个有效值,需要被及时清理。变量可以为 nil。
// 如果 havenew 是 true, 需要一个新的 value 来替换变量。变量可以为 nil
// 如果crashifdeallocation 是 ture ,那么如果 newobj 是 deallocating,或者 newobj 的类不支持弱引用,则该进程就会停止。
// 如果crashifdeallocation 是 false,那么 nil 会被存储。
template
<
bool
haveold,
bool
havenew,
bool
crashifdeallocating>
static
id storeweak(id *location, objc_object *newobj)
{
assert
(haveold || havenew);
if
(!havenew)
assert
(newobj == nil);
class
previouslyinitializedclass = nil;
id oldobj;
// 创建新旧散列表
sidetable *oldtable;
sidetable *newtable;
// acquire locks for old and new values.
// 获得新值和旧值的锁存位置 (用地址作为唯一标示)
// order by lock address to prevent lock ordering problems.
// 通过地址来建立索引标志,防止桶重复
// retry if the old value changes underneath us.
// 下面指向的操作会改变旧值
retry:
if
(haveold) {
// 如果 haveold 为 true ,更改指针,获得以 oldobj 为索引所存储的值地址
oldobj = *location;
oldtable = &sidetables()[oldobj];
}
else
{
oldtable = nil;
}
if
(havenew) {
// 获得以 newobj 为索引所存储的值对象
newtable = &sidetables()[newobj];
}
else
{
newtable = nil;
}
// 对两个 table 进行加锁操作,防止多线程中竞争冲突
sidetable::locktwo<haveold, havenew>(oldtable, newtable);
// location 应该与 oldobj 保持一致,如果不同,说明当前的 location 已经处理过 oldobj 可是又被其他线程所修改, 保证线程安全,这个判断用来避免线程冲突重处理问题
if
(haveold && *location != oldobj) {
sidetable::unlocktwo<haveold, havenew>(oldtable, newtable);
goto
retry;
}
// prevent a deadlock between the weak reference machinery
// and the +initialize machinery by ensuring that no
// weakly-referenced object has an un-+initialized isa.
// 防止弱引用之间发生死锁,并且通过 +initialize 初始化构造器保证所有弱引用的 isa 非空指向
if
(havenew && newobj) {
// 获得新对象的 isa 指针
class
cls = newobj->getisa();
// 判断 isa 非空且已经初始化
if
(cls != previouslyinitializedclass &&
!((objc_class *)cls)->isinitialized())
{
// 对两个表解锁
sidetable::unlocktwo<haveold, havenew>(oldtable, newtable);
_class_initialize(_class_getnonmetaclass(cls, (id)newobj));
// if this class is finished with +initialize then we're good.
// if this class is still running +initialize on this thread
// (i.e. +initialize called storeweak on an instance of itself)
// then we may proceed but it will appear initializing and
// not yet initialized to the check above.
// instead set previouslyinitializedclass to recognize it on retry.
// 如果该类已经完成执行 +initialize 方法是最好的,如果该类 + initialize 在线程中,例如 +initialize 正在调用storeweak 方法,那么则需要手动对其增加保护策略,并设置 previouslyinitializedclass 指针进行标记然后重新尝试
previouslyinitializedclass = cls;
goto
retry;
}
}
// clean up old value, if any. 清除旧值
if
(haveold) {
weak_unregister_no_lock(&oldtable->weak_table, oldobj, location);
}
// assign new value, if any. 分配新值
if
(havenew) {
newobj = (objc_object *)weak_register_no_lock(&newtable->weak_table,
(id)newobj, location,
crashifdeallocating);
// weak_register_no_lock returns nil if weak store should be rejected
// 如果弱引用被释放则该方法返回 nil
// set is-weakly-referenced bit in refcount table.
// 在引用计数表中设置弱引用标记位
if
(newobj && !newobj->istaggedpointer()) {
newobj->setweaklyreferenced_nolock();
}
// do not set *location anywhere else. that would introduce a race.
*location = (id)newobj;
}
else
{
// no new value. the storage is not changed.
}
sidetable::unlocktwo<haveold, havenew>(oldtable, newtable);
return
(id)newobj;
}
|
以上就是 store_weak 这个函数的实现,它主要做了以下几件事:
初始化弱引用对象流程一览 。
弱引用的初始化,从上文的分析可以看出,主要的操作部分就是在弱引用表的取键、查询散列、创建弱引用等操作,可以总结出如下的流程图:
旧对象解除注册操作 weak_unregister_no_lock 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
void
weak_unregister_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
weak_entry_t *entry;
if
(!referent)
return
;
if
((entry = weak_entry_for_referent(weak_table, referent))) {
remove_referrer(entry, referrer);
bool
empty =
true
;
if
(entry->out_of_line && entry->num_refs != 0) {
empty =
false
;
}
else
{
for
(
size_t
i = 0; i < weak_inline_count; i++) {
if
(entry->inline_referrers[i]) {
empty =
false
;
break
;
}
}
}
if
(empty) {
weak_entry_remove(weak_table, entry);
}
}
// do not set *referrer = nil. objc_storeweak() requires that the
// value not change.
}
|
该方法主要作用是将旧对象在 weak_table 中接触 weak 指针的对应绑定。根据函数名,称之为解除注册操作.
来看看这个函数的逻辑。首先参数是 weak_table_t 表,键和值。声明 weak_entry_t 变量,如果key,也就是引用计数对象为空,直接返回。根据全局入口表和键获取对应的 weak_entry_t 对象,也就是 weak 表记录。获取到记录后,将记录表以及 weak 对象作为参数传入 remove_referrer 函数中,这个函数就是解除操作。然后判断这个 weak 记录是否为空,如果为空,从全局记录表中清除相应的引用计数对象的 weak 记录表.
接下来,我们了解一下,如何获取这个 weak_entry_t 这个变量.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
static
weak_entry_t *weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert
(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if
(!weak_entries)
return
nil;
size_t
index = hash_pointer(referent) & weak_table->mask;
size_t
hash_displacement = 0;
while
(weak_table->weak_entries[index].referent != referent) {
index = (index+1) & weak_table->mask;
hash_displacement++;
if
(hash_displacement > weak_table->max_hash_displacement) {
return
nil;
}
}
return
&weak_table->weak_entries[index];
}
|
这个函数的逻辑就是先获取全局 weak 表入口,然后将引用计数对象的地址进行 hash 化后与 weak_table->mask 做与操作,作为下标,在全局 weak 表中查找,若找到,返回这个对象的 weak 记录表,若没有,返回nil.
再来了解一下解除对象的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
static
void
remove_referrer(weak_entry_t *entry, objc_object **old_referrer)
{
if
(! entry->out_of_line) {
for
(
size_t
i = 0; i < weak_inline_count; i++) {
if
(entry->inline_referrers[i] == old_referrer) {
entry->inline_referrers[i] = nil;
return
;
}
}
_objc_inform(
"attempted to unregister unknown __weak variable "
"at %p. this is probably incorrect use of "
"objc_storeweak() and objc_loadweak(). "
"break on objc_weak_error to debug.\n"
,
old_referrer);
objc_weak_error();
return
;
}
size_t
index = w_hash_pointer(old_referrer) & (entry->mask);
size_t
hash_displacement = 0;
while
(entry->referrers[index] != old_referrer) {
index = (index+1) & entry->mask;
hash_displacement++;
if
(hash_displacement > entry->max_hash_displacement) {
_objc_inform(
"attempted to unregister unknown __weak variable "
"at %p. this is probably incorrect use of "
"objc_storeweak() and objc_loadweak(). "
"break on objc_weak_error to debug.\n"
,
old_referrer);
objc_weak_error();
return
;
}
}
entry->referrers[index] = nil;
entry->num_refs--;
}
|
这个函数传入的是 weak 对象,当 out_of_line 为0 时,遍历数组,找到对应的对象,置nil,如果未找到,报错并返回。当 out_of_line 不为0时,根据对象的地址 hash 化并和 mask 做与操作作为下标,查找相应的对象,若没有,报错并返回,若有,相应的置为 nil,并减少元素数量,即 num_refs 减 1.
新对象添加注册操作 weak_register_no_lock 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
id weak_register_no_lock(weak_table_t *weak_table, id referent_id,
id *referrer_id,
bool
crashifdeallocating)
{
objc_object *referent = (objc_object *)referent_id;
objc_object **referrer = (objc_object **)referrer_id;
if
(!referent || referent->istaggedpointer())
return
referent_id;
// ensure that the referenced object is viable
bool
deallocating;
if
(!referent->isa()->hascustomrr()) {
deallocating = referent->rootisdeallocating();
}
else
{
bool
(*allowsweakreference)(objc_object *, sel) =
(
bool
(*)(objc_object *, sel))
object_getmethodimplementation((id)referent,
sel_allowsweakreference);
if
((imp)allowsweakreference == _objc_msgforward) {
return
nil;
}
deallocating =
! (*allowsweakreference)(referent, sel_allowsweakreference);
}
if
(deallocating) {
if
(crashifdeallocating) {
_objc_fatal(
"cannot form weak reference to instance (%p) of "
"class %s. it is possible that this object was "
"over-released, or is in the process of deallocation."
,
(
void
*)referent, object_getclassname((id)referent));
}
else
{
return
nil;
}
}
// now remember it and where it is being stored
weak_entry_t *entry;
if
((entry = weak_entry_for_referent(weak_table, referent))) {
append_referrer(entry, referrer);
}
else
{
weak_entry_t new_entry;
new_entry.referent = referent;
new_entry.out_of_line = 0;
new_entry.inline_referrers[0] = referrer;
for
(
size_t
i = 1; i < weak_inline_count; i++) {
new_entry.inline_referrers[i] = nil;
}
weak_grow_maybe(weak_table);
weak_entry_insert(weak_table, &new_entry);
}
// do not set *referrer. objc_storeweak() requires that the
// value not change.
return
referent_id;
}
|
一大堆 if-else, 主要是为了判断该对象是不是 taggedpoint 以及是否正在调用 dealloca 等。下面操作开始,同样是先获取 weak 表记录,如果获取到,则调用 append_referrer 插入对象,若没有,则新建一个 weak 表记录,默认为 out_of_line,然后将新对象放到 0 下标位置,其他位置置为 nil 。下面两个函数 weak_grow_maybe 是用来判断是否需要重申请内存重 hash,weak_entry_insert 函数是用来将新建的 weak 表记录插入到全局 weak 表中。插入时同样是以对象地址的 hash 化和 mask 值相与作为下标来记录的.
接下来看看 append_referrer 函数,源代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
static
void
append_referrer(weak_entry_t *entry, objc_object **new_referrer)
{
if
(! entry->out_of_line) {
// try to insert inline.
for
(
size_t
i = 0; i < weak_inline_count; i++) {
if
(entry->inline_referrers[i] == nil) {
entry->inline_referrers[i] = new_referrer;
return
;
}
}
// couldn't insert inline. allocate out of line.
weak_referrer_t *new_referrers = (weak_referrer_t *)
calloc
(weak_inline_count,
sizeof
(weak_referrer_t));
// this constructed table is invalid, but grow_refs_and_insert
// will fix it and rehash it.
for
(
size_t
i = 0; i < weak_inline_count; i++) {
new_referrers[i] = entry->inline_referrers[i];
}
entry->referrers = new_referrers;
entry->num_refs = weak_inline_count;
entry->out_of_line = 1;
entry->mask = weak_inline_count-1;
entry->max_hash_displacement = 0;
}
assert
(entry->out_of_line);
if
(entry->num_refs >= table_size(entry) * 3/4) {
return
grow_refs_and_insert(entry, new_referrer);
}
size_t
index = w_hash_pointer(new_referrer) & (entry->mask);
size_t
hash_displacement = 0;
while
(entry->referrers[index] != null) {
index = (index+1) & entry->mask;
hash_displacement++;
}
if
(hash_displacement > entry->max_hash_displacement) {
entry->max_hash_displacement = hash_displacement;
}
weak_referrer_t &ref = entry->referrers[index];
ref = new_referrer;
entry->num_refs++;
}
|
当 out_of_line 为 0,并且静态数组里面还有位置存放,那么直接存放并返回。如果没有位置存放,则升级为动态数组,并加入。如果 out_of_line 不为 0,先判断是否需要扩容,然后同样的,使用对象地址的 hash 化和 mask 做与操作作为下标,找到相应的位置并插入.
对象的销毁以及 weak 的置 nil 实现 。
释放时,调用cleardeallocating函数。cleardeallocating 函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录.
当weak引用指向的对象被释放时,又是如何去处理weak指针的呢?当释放对象时,其基本流程如下:
objc_clear_deallocating的具体实现如下:
1
2
3
4
5
6
7
8
|
void
objc_clear_deallocating(id obj)
{
assert
(obj);
assert
(!usegc);
if
(obj->istaggedpointer())
return
;
obj->cleardeallocating();
}
|
这个函数只是做一些判断以及更深层次的函数调用, 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void
objc_object::sidetable_cleardeallocating()
{
sidetable& table = sidetables()[
this
];
// clear any weak table items
// clear extra retain count and deallocating bit
// (fixme warn or abort if extra retain count == 0 ?)
table.lock();
// 迭代器
refcountmap::iterator it = table.refcnts.find(
this
);
if
(it != table.refcnts.end()) {
if
(it->second & side_table_weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)
this
);
}
table.refcnts.erase(it);
}
table.unlock();
}
|
我们可以看到,在这个函数中,首先取出对象对应的sidetable实例,如果这个对象有关联的弱引用,则调用weak_clear_no_lock来清除对象的弱引用信息,我们在来深入一下, 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if
(entry == nil) {
/// xxx shouldn't happen, but does with mismatched cf/objc
//printf("xxx no entry for clear deallocating %p\n", referent);
return
;
}
// zero out references
weak_referrer_t *referrers;
size_t
count;
if
(entry->out_of_line) {
referrers = entry->referrers;
count = table_size(entry);
}
else
{
referrers = entry->inline_referrers;
count = weak_inline_count;
}
for
(
size_t
i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if
(referrer) {
if
(*referrer == referent) {
*referrer = nil;
}
else
if
(*referrer) {
_objc_inform(
"__weak variable at %p holds %p instead of %p. "
"this is probably incorrect use of "
"objc_storeweak() and objc_loadweak(). "
"break on objc_weak_error to debug.\n"
,
referrer, (
void
*)*referrer, (
void
*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
|
这个函数根据 out_of_line 的值,取得对应的记录表,然后根据引用计数对象,将相应的 weak 对象置 nil。最后清除相应的记录表.
通过上面的描述,我们基本能了解一个weak引用从生到死的过程。从这个流程可以看出,一个weak引用的处理涉及各种查表、添加与删除操作,还是有一定消耗的。所以如果大量使用__weak变量的话,会对性能造成一定的影响。那么,我们应该在什么时候去使用weak呢?《objective-c高级编程》给我们的建议是只在避免循环引用的时候使用__weak修饰符.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:https://www.jianshu.com/p/dce1bec2199e 。
最后此篇关于iOS 中weak的实现代码示例的文章就讲到这里了,如果你想了解更多关于iOS 中weak的实现代码示例的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!